jQuery UI :tabbable -- what the?

This week I've been working on adding some basic accessibility tweaks to our site. Instead of starting from scratch, I've been peeking at the jQuery UI code, because I know those guys have been working with the ARIA blokes to "accessibleize" jQuery UI. I think that's greate for everybody, by the way, for specifically this reason. Instead of having just the specs alone, I can study the standards and also look at how some real-world coders have implemented those standards.

Anyway, the trick I wanted to duplicate was the dialog's auto-focus trick. When a jQuery UI dialog pops up it automatically shifts tab focus to the first element in the dialog that can receive focus, which is nice for accessibility because then even a non-visual user's attention is brought to the dialog. I could have thrown together a quick selector that checks for the first anchor, input element or whatever, but like I said, I wanted to see what they'd done.

I snuck into the source, and noticed they're using a selector filter called ":tabbable" to select the elements that, I assumed, was capable of being tabbed to. A quick Google search and I found nothing -- hence this post.

Apparently jQuery UI is adding two very useful (for accessibility purposes, anyway) selector filters to the jQuery selector engine, ":focusable" and ":tabbable". Here's the code:

//Additional selectors
$.extend($.expr[':'], {
data: function(elem, i, match) {
return !!$.data(elem, match[3]);
},

focusable: function(element) {
var nodeName = element.nodeName.toLowerCase(),
tabIndex = $.attr(element, 'tabindex');
return (/input|select|textarea|button|object/.test(nodeName)
? !element.disabled
: 'a' == nodeName || 'area' == nodeName
? element.href || !isNaN(tabIndex)
: !isNaN(tabIndex))
// the element and all of its ancestors must be visible
// the browser may report that the area is hidden
&& !$(element)['area' == nodeName ? 'parents' : 'closest'](':hidden').length;
},

tabbable: function(element) {
var tabIndex = $.attr(element, 'tabindex');
return (isNaN(tabIndex) || tabIndex >= 0) && $(element).is(':focusable');
}
});


:focusable selects the list of elements that are inherently focusable, i.e. anchor tags, input elements, form elements that aren't disabled, or elements that have a defined "tabindex" attribute. :tabbable is a subset of this list, only including those :focusable elements that have tabindex >= 0. You can set an element to have tabindex="-1" and it will be focusable, but won't show up anywhere in the page's tab order, hence it's not tabbable.

Anyway, cool stuff. If you use this you'll probably want to check ":visible" as well.

Comments

Unknown said…
One thing to note about :tabbable, It limits the subset of elements when inside a MODAL dialog. When tabbing through a large (hundreds of rows/cols) table with tabbale controls in each cell, performance is terrible.

Not sure that there is any way around it. Really, I just wanted to comment because your names are similar and I've been called a Whitmer in the past.

-Brandon Wittwer

Popular Posts