AngularJs: Keyboard navigation using TAB key in different groups/forms/divs

10,756

Solution 1

Many thanks @Kiran Nawaleand @Gökhan Kurt for guiding me to the solution.

I have created a generic directive which is reusable for any angular app.

Dependencies

  1. Jquery
  2. AngularJs

In directive I have added comments which will guide you through the way the directive is working.

How to use?

Add the given below attributes and the directive in your element

tab-man tab-index="0" tab-group="g1"

tab-man : the directive
tab-index : it is the index of the element in the group
tab-group : name of the group

Note:

  1. There should always be a 0 index in every group otherwise the cycle will not restart.

  2. If any index is skipped like 0,1,2,4... (3 is skipped) then after 2 the focus move to 0

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
  <script>
    var app = angular.module('myapp', []); /// angular app


    app.directive('tabMan', function() { ///directive
      return {
        restrict: 'A', /// accessible only by attribute
        scope: {}, /// scope is not needed

        link: function(scope, element, attrs) { ///link function to add key-down event on the target element

          var gotoElement = function(grp, idx) {
            /// get next element
            var nextElement = $("input[tab-group='" + grp + "'][tab-index='" + idx + "']")[0];

            /// if there is not next element then go to the 0 index
            if (nextElement == undefined) {
              if (idx != 0) { /// if the index is 0 then do not do any thing
                gotoElement(grp, 0); /// restart the cycle
              }
            } else {
              nextElement.focus(); /// succesfully give focus to the next
            }

          };

          var tabIndex = attrs.tabIndex;
          var tabGroup = attrs.tabGroup;

          $(element).keydown(function(event) {

            if (event.keyCode == 9) { /// go inside if tab key is pressed

              var tIdx = $(event.target).attr("tab-index"); /// get the current index of element
              var nextTid = parseInt(tIdx.toString()) + 1; /// get the next index of element
              nextTid = Number(nextTid); /// turn the index into number

              var tGrp = $(event.target).attr("tab-group"); /// get the group of the element

              gotoElement(tGrp, nextTid); /// go to the next element

              /// the work of tab is done by the directive so remove the default and stop the bubbeling
              event.stopPropagation()
              event.preventDefault();
            }

          });
        }
      };
    });
  </script>
</head>

<body ng-app="myapp">
  <div role="group" tabindex="-1">
    <h1>Group 1</h1>
    <br>
    <input type="text" tab-man tab-index="0" tab-group="g1" />
    <br>
    <input type="text" tab-man tab-index="5" tab-group="g1" />
    <br>
    <input type="text" tab-man tab-man tab-index="2" tab-group="g1" />
    <br>
    <input type="text" tab-man tab-index="3" tab-group="g1" />
    <br>
    <input type="text" tab-man tab-index="4" tab-group="g1" />
    <br>
    <input type="text" tab-man tab-index="1" tab-group="g1" />
    <br>
    <button>Submit</button>
  </div>
  <hr>

  <div>
    <div role="group" tabindex="-1">
      <h1>Group 2</h1>
      <br>
      <input type="text" tab-man tab-index="0" tab-group="g2" />
      <br>
      <input type="text" tab-man tab-index="5" tab-group="g2" />
      <br>
      <input type="text" tab-man tab-man tab-index="2" tab-group="g2" />
      <br>
      <input type="text" tab-man tab-index="3" tab-group="g2" />
      <br>
      <input type="text" tab-man tab-index="4" tab-group="g2" />
      <br>
      <input type="text" tab-man tab-index="1" tab-group="g2" />
      <br>
      <button>Submit</button>
    </div>
  </div>
</body>

Solution 2

Unfortunately, you can't do that without JavaScript.

Here I have implemented the JavaScript/jQuery code to handle tabbing between two groups.

Also please make a note that For moving to the second group you/user have to decide when to move to second group of inputs.

HTML

<div id="group-1" role="group" tabindex="0">
  <h1>Group 1</h1>
  <br>
  <input type="text" tabindex="1" />
  <br>
  <input type="text" tabindex="1" />
  <br>
  <input type="text" tabindex="1" />
  <br>
  <input type="text" tabindex="1" />
  <br>
  <input type="text" tabindex="1" />
  <br>
  <input type="text" tabindex="1" />
  <br>
  <input type="button" value="submit" tabindex="1" />
  <div id="focusguard-1" tabindex="1"></div>
</div>
<hr>

<div>
  <div id="group-2" role="group" tabindex="0">
    <h1>Group 2</h1>
    <br>
    <input type="text" tabindex="1" />
    <br>
    <input type="text" tabindex="1" />
    <br>
    <input type="text" tabindex="1" />
    <br>
    <input type="text" tabindex="1" />
    <br>
    <input type="text" tabindex="1" />
    <br>
    <input type="text" tabindex="1" />
    <br>
    <input type="button" value="submit" tabindex="1" />
    <div id="focusguard-2" tabindex="1"></div>
  </div>
</div>

JavaScript/JQuery

$('#focusguard-1').on('focus', function() {
  $('#group-1 input:first').focus();
});

$('#focusguard-2').on('focus', function() {
  console.log("Focus in");
  $('#group-2 input:first').focus();
});

Solution 3

tabindex works globally in a window. You can achieve what you want with javascript. Your groups must have a class "closedFocus" and your elements should have a tabIndex. You can change the code to achieve the effect you want:

$(".closedFocusGroup [tabindex]").on("keydown", function(e) {
  if (e.which == 9) {
    var parentGroup = this.closest(".closedFocusGroup");
    var allChildren = $(parentGroup).find("[tabindex]");
    allChildren.sort(function(a,b){ return a.tabIndex-b.tabIndex});
    var thisIndex = allChildren.index(this);
    var nextIndex = (thisIndex + 1) % allChildren.length;
    var nextItem = allChildren[nextIndex];
    if (nextItem) nextItem.focus();
    e.preventDefault();
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div role="group" class="closedFocusGroup" tabindex="-1">
  <h1>Group 1</h1>
  <br>
  <input type="text" tabindex="1" />
  <br>
  <input type="text" tabindex="6" />
  <br>
  <input type="text" tabindex="4" />
  <br>
  <input type="text" tabindex="2" />
  <br>
  <input type="text" tabindex="5" />
  <br>
  <input type="text" tabindex="2" />
  <br>
  <button tabindex="7">Submit</button>
</div>
<hr>

<div>
  <div role="group" class="closedFocusGroup" tabindex="-1">
    <h1>Group 2</h1>
    <br>
    <input type="text" tabindex="1" />
    <br>
    <input type="text" tabindex="6" />
    <br>
    <input type="text" tabindex="4" />
    <br>
    <input type="text" tabindex="2" />
    <br>
    <input type="text" tabindex="5" />
    <br>
    <input type="text" tabindex="2" />
    <br>
    <button tabindex="7">Submit</button>
  </div>
</div>

Share:
10,756
Vikas Bansal
Author by

Vikas Bansal

javascript, typescript, ReactJs, Angularjs, Nodejs, Electronjs.... waiting for the next :) :P :D

Updated on July 22, 2022

Comments

  • Vikas Bansal
    Vikas Bansal almost 2 years

    I have two groups and I want to separate navigation for them and on last tab control of the group when tab key is pressed then the iteration cycle should be restarted and the focus should move to the initial element of the group (which would be the 0 index)

    In given below Example I have added two groups and in the group I have added some text-boxes and assigned not serial order.

    Problems

    1. When Pressing tab focus is moving across the groups
    2. On the last index the cycle is not restarting, instead its going in the address bar

    Note: I am making an angularjs app and this is just a dummy to provide a clear view of my problem

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    
    <body>
        <div role="group" tabindex="-1">
            <h1>Group 1</h1>
            <br>
            <input type="text" tabindex="1" />
            <br>
            <input type="text" tabindex="6" />
            <br>
            <input type="text" tabindex="4" />
            <br>
            <input type="text" tabindex="2" />
            <br>
            <input type="text" tabindex="5" />
            <br>
            <input type="text" tabindex="2" />
            <br>
            <button tabindex="7">Submit</button>
        </div>
        <hr>
    
        <div>
            <div role="group" tabindex="-1">
                <h1>Group 2</h1>
                <br>
                <input type="text" tabindex="1" />
                <br>
                <input type="text" tabindex="6" />
                <br>
                <input type="text" tabindex="4" />
                <br>
                <input type="text" tabindex="2" />
                <br>
                <input type="text" tabindex="5" />
                <br>
                <input type="text" tabindex="2" />
                <br>
                <button tabindex="7">Submit</button>
            </div>
        </div>
    </body>
    
    </html>

  • Neelam Sharma
    Neelam Sharma almost 7 years
    I have tried above code to restart, but the cursor navigate top to down, I need cursor movement left-to-right then down, how can I achieve it
  • Vikas Bansal
    Vikas Bansal almost 7 years
    you can set tab-index="2"
  • Neelam Sharma
    Neelam Sharma almost 7 years
    Thanks, it working fine to restart the cycle, but my issue is : stackoverflow.com/questions/44455082/…, how can I navigate left-to-right then down when design is in different div using your directive?