Search code examples
htmltabstabindex

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 form 1's tab index 1, 2, and 3, and then move back to 1 within the same form, then 2, etc., never going to form 2's inputs. I also don't want to change the tab indices.

<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>

Solution

  • 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
      }
    }
    <form style="background-color: red" data-tabgroup>
      <input tabindex="01" />
      <input tabindex="02" />
      <input tabindex="03" />
    </form>
    
    <form style="background-color: blue" data-tabgroup>
      <input tabindex="01" />
      <input tabindex="02" />
      <input tabindex="03" />
    </form>

    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.