Focus Next Element In Tab Index

Here's something I build for this purpose:

function focusNextElement () {
    //add all elements we want to include in our selection
    var focussableElements = 'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])';
    if (document.activeElement && document.activeElement.form) {
        var focussable = Array.prototype.filter.call(document.activeElement.form.querySelectorAll(focussableElements),
        function (element) {
            //check for visibility while always include the current activeElement 
            return element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement
        });
        var index = focussable.indexOf(document.activeElement);
        if(index > -1) {
           var nextElement = focussable[index + 1] || focussable[0];
           nextElement.focus();
        }                    
    }
}

Features:

  • configurable set of focusable elements
  • no jQuery needed
  • works in all modern browsers
  • fast & lightweight

I created a simple jQuery plugin which does just this. It uses the ':tabbable' selector of jQuery UI to find the next 'tabbable' element and selects it.

Example usage:

// Simulate tab key when element is clicked 
$('.myElement').bind('click', function(event){
    $.tabNext();
    return false;
});

Without jquery: First of all, on your tab-able elements, add class="tabable" this will let us select them later. (Do not forget the "." class selector prefix in the code below)

var lastTabIndex = 10;
function OnFocusOut()
{
    var currentElement = $get(currentElementId); // ID set by OnFOcusIn
    var curIndex = currentElement.tabIndex; //get current elements tab index
    if(curIndex == lastTabIndex) { //if we are on the last tabindex, go back to the beginning
        curIndex = 0;
    }
    var tabbables = document.querySelectorAll(".tabable"); //get all tabable elements
    for(var i=0; i<tabbables.length; i++) { //loop through each element
        if(tabbables[i].tabIndex == (curIndex+1)) { //check the tabindex to see if it's the element we want
            tabbables[i].focus(); //if it's the one we want, focus it and exit the loop
            break;
        }
    }
}

I've never implemented this, but I've looked into a similar problem, and here's what I would try.

Referencing the jQuery implementation, you must:

  1. Listen for Tab and Shift+Tab
  2. Know which elements are tab-able
  3. Understand how tab order works

1. Listen for Tab and Shift+Tab

Listening for Tab and Shift+Tab are probably well-covered elsewhere on the web, so I'll skip that part.

2. Know which elements are tab-able

Knowing which elements are tab-able is trickier. Basically, an element is tab-able if it is focusable and does not have the attribute tabindex="-1" set. So then we must ask which elements are focusable. The following elements are focusable:

  • input, select, textarea, button, and object elements that aren't disabled.
  • a and area elements that have an href or have a numerical value for tabindex set.
  • any element that has a numerical value for tabindex set.

Furthermore, an element is focusable only if:

  • None of its ancestors are display: none.
  • The computed value of visibility is visible. This means that the nearest ancestor to have visibility set must have a value of visible. If no ancestor has visibility set, then the computed value is visible.

More details are in another Stack Overflow answer.

3. Understand how tab order works

The tab order of elements in a document is controlled by the tabindex attribute. If no value is set, the tabindex is effectively 0.

The tabindex order for the document is: 1, 2, 3, …, 0.

Initially, when the body element (or no element) has focus, the first element in the tab order is the lowest non-zero tabindex. If multiple elements have the same tabindex, you then go in document order until you reach the last element with that tabindex. Then you move to the next lowest tabindex and the process continues. Finally, finish with those elements with a zero (or empty) tabindex.