jQuery UI Cookbook
上QQ阅读APP看书,第一时间看更新

Section navigation using the Tab key

In most desktop environments, the Tab key is a secret weapon in navigation—one tool that many users are accustomed to. Likewise, we can utilize the Tab key in HTML5 applications using the tabindex property. This tells the browser the order in which to focus elements, each time the key is pressed.

Unfortunately, this isn't as straightforward as it looks with accordion widgets. We can't specify a tabindex value in each section header and expect the Tab key events to work as expected. Instead, the default widget implementation provides a different kind of key navigation—the up and down arrow keys. Ideally, it would be useful to give users the ability to navigate through the accordion sections using the Tab key that they're familiar with, while preserving the default key navigation provided by the widget.

Getting ready

To get started, we'll want a basic accordion; ideally, something simple that has basic content within each section, so that we can visually see how the Tab key behavior works before we implement custom events, and afterward.

As a guide, here is my basic accordion markup:

<div id="accordion">
    <h3>Section 1</h3>
    <div>
        <p>Section 1 content</p>
    </div>
    <h3>Section 2</h3>
    <div>
        <p>Section 2 content</p>
    </div>
    <h3>Section 3</h3>
    <div>
        <p>Section 3 content</p>
    </div>
    <h3>Section 4</h3>
    <div>
        <p>Section 4 content</p>
    </div>
</div>

And, here is the code used to instantiate the accordion widget:

$(function() {

    $( "#accordion" ).accordion({
        collapsible: true
    });

});
Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

We now have a basic collapsible accordion widget that we can look at in the browser. The reason we're adding the collapsible option here is so that we can experiment with the key navigation—we get a better view of which section is in focus when all are collapsed. You can see how the up and down arrow keys allow the user to traverse through the accordion sections while the Tab key has no effect. Let's change that.

How to do it...

We're going to extend the accordion widget to include an event handler for keypress events. The default accordion implementation has keypress events for dealing with the up, down, left, right, and Enter keys. We don't need to change that. Instead, we add our own handler that understands what to do when the Tab key and Shift + Tab keys are pressed.

Look at the following code:

(function( $, undefined ) {

$.widget( "ab.accordion", $.ui.accordion, {

    _create: function () {

        this._super( "_create" );
        this._on( this.headers, { keydown: "_tabkeydown" } );

    },

    _tabkeydown: function ( event ) {

        if ( event.altKey || event.ctrlKey ) {
            return;
         }

        if ( event.keyCode !== $.ui.keyCode.TAB ) {
            return;
        }

        var headers = this.headers,
            headerLength = headers.length,
            headerIndex = headers.index( event.target ),
            toFocus = false;

        if ( event.shiftKey && headerIndex - 1 >= 0 ) {
            toFocus = headers[ headerIndex - 1 ];
        }

        if ( !event.shiftKey && headerIndex + 1 < headerLength ) {
            toFocus = headers[ headerIndex + 1 ];
        }

        if ( toFocus ) {

            $( event.target ).attr( "tabIndex", -1 );
            $( toFocus ).attr( "tabIndex", 0 );
            toFocus.focus();
            event.preventDefault();

        }

    }

});

})( jQuery );

$(function() {

    $( "#accordion" ).accordion({
        collapsible: true
    });

});

How it works...

We're creating a new accordion widget here by extending the default accordion widget. The advantage to this approach of extending the accordion widget is that we're not tinkering with instances of the widget; all accordion instances will acquire this new behavior.

The _create() method is replaced with our new implementation of it. The first thing we do in this replacement method is call the original _create() method. We don't want to prevent the default setup actions of the accordion widget from taking place. So, using _super() we're able to do that. The next thing we do is bind our new tabkeydown() event handler to the keydown event.

The tabkeydown() handler is a simplified version of the keydown event handler provided in the original accordion implementation. If the Alt or the Ctrl key was pressed in combination with another key, we ignore the event. If the key press was anything other than a Tab, we ignore the event too, since we're only interested in altering the Tab key behavior when one of the accordion headers is in focus.

The guts of the handler determine what should happen when the Tab key is pressed. In which direction should we move the accordion header focus? When do we ignore the event and let the default browser behavior take over? The trick is, figuring out our current index position. If we're on the first header and the user presses Shift + Tab, meaning they want to traverse backward, then we don't do anything. Likewise, if we're on the last header and the user presses Tab, we pass control back to the browser so as not to interfere with the expected functionality.