Focus Next Element In Tab Index
I am trying to move the focus to the next element in the tab sequence based upon the current element which has focus. Thus far I have not turned up anything in my searches.
function OnFocusOut()
{
var currentElement = $get(currentElementId); // ID set by OnFocusIn
currentElementId = "";
currentElement.nextElementByTabIndex.focus();
}
Of course the nextElementByTabIndex is the key part for this to work. How do I find the next element in the tab sequence? The solution would need to be based using JScript and not something like JQuery.
Solution 1:
I've never implemented this, but I've looked into a similar problem, and here's what I would try.
Try this first
First, I would see if you could simply fire a keypress
event for the Tab key on the element that currently has focus. There may be a different way of doing this for different browsers.
If that doesn't work, you'll have to work harder…
Referencing the jQuery implementation, you must:
- Listen for Tab and Shift+Tab
- Know which elements are tab-able
- 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
, andobject
elements that aren't disabled. -
a
andarea
elements that have anhref
or have a numerical value fortabindex
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
isvisible
. This means that the nearest ancestor to havevisibility
set must have a value ofvisible
. If no ancestor hasvisibility
set, then the computed value isvisible
.
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
.
Solution 2:
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
Solution 3:
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;
}
}
}
Solution 4:
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;
});
Solution 5:
The core of the answer lies on finding the next element:
function findNextTabStop(el) {
var universe = document.querySelectorAll('input, button, select, textarea, a[href]');
var list = Array.prototype.filter.call(universe, function(item) {return item.tabIndex >= "0"});
var index = list.indexOf(el);
return list[index + 1] || list[0];
}
Usage:
var nextEl = findNextTabStop(element);
nextEl.focus();
Notice I don't care about prioritizing tabIndex
.