santosh santosh - 1 month ago 8
HTML Question

Keep focus looping on inputs of form when using tab instead of moving to second form

I want to restrict the focus to a single form when tab is pressed.

By that I mean focus on 1, 2, and 3 inputs of form 1 then again move to 1, 2, and 3 tabs of form 1, never going to form 2's inputs. I also don't want to change the tab indexes.

<form style="background-color:red">
<input tabindex="01"/>
<input tabindex="02"/>
<input tabindex="03"/>
</form>

<form style="background-color:blue">
<input tabindex="01"/>
<input tabindex="02"/>
<input tabindex="03"/>
</form>

Answer

This isn't good practice, so don't do it unless there is really good reason to do it...


But there's no built in HTML way to do this, so we need to use some JavaScript along with a custom data attribute.

I think it's easiest to use a data attribute to the parent (the <form>) so that way we don't have to add it to each input individually. I named mine data-tabgroup.

Then we need some JS:

// Select the tab groups based on the data attribute we added
var tabgroups = document.querySelectorAll("[data-tabgroup]");

// Loop through each to attach the listeners we need
for (var i = 0; i < tabgroups.length; i++) {
  var inputs = tabgroups[i].querySelectorAll("[tabindex]");

  // Loop through all of the elements we want the tab to be changed for
  for (var j = 0; j < inputs.length; j++) {

    // Listen for the tab pressed on these elements
    inputs[j].addEventListener("keydown", function(myIndex, inputs, e) {
      if (e.key === "Tab") {
        // Prevent the default tab behavior
        e.preventDefault();

        // Focus the next one in the group
        if (inputs[myIndex + 1]) {
          inputs[myIndex + 1].focus();
        } else { // Or focus the first one again
          inputs[0].focus();
        }
      }
    }.bind(null, j, inputs)) // Make a copy of the variables to use in the addEventListener
  }
}

And that's it! Here's the demo.


Some notes:

  • The current implementation ignores the value of the tabindex within the group (it just selects the next one in the HTML). To take that into account, you just need to put the elements in the array in order of their tabindexes or sort them by the tabindexes after you add them to the array.
  • The current implementation requires that a tabindex be applied to children that you want this to affect. If you want it to apply to all inputs by default, just change the querySelectorAll value for inputs to input. If you want something more complex, you'll have to change it as needed.
Comments