Video published 2009-10-29.
Todd Kloots: Welcome everybody. We're going to talk about building accessible widgets with YUI 3. My name is Todd Kloots, one of the members of the core YUI team here at Yahoo! Let's dive into some of the utilities that we've created for you to make more accessible widgets.
One of the accessibility features you're going to want to consider first and foremost when addressing accessibility for your user interface is keyboard access. If you're not already convinced that keyboard access is important for all users, be that users with disabilities or users without disabilities, here are a few use cases that kind of drive that point home.
Electronic voice: Minor desktop applications selected builder: Macintosh HD selected. Override [?] selected folder, to do selected folder.
Todd: What you just heard is the voiceover screen reader from Mac OS X navigating the desktop, so that is the user experience for a user of a screen reader. Since blind users don't have the ability to see what's on the screen, they rely on the keyboard to navigate, obviously, so keyboard access for that particular subset of users is, as a result, a necessity. That's one very important example for keyboard accessibility.
Another use case, for example, might be users who don't have a disability but essentially are using their computer in a way that might not be your standard set up. For example, let's say I'm using my computer as a media hub at home. I can sort of operate a mouse as I'm lounging on the couch, but it's not necessarily terribly efficient and it might be somewhat clumsy. Better interaction would probably come through use of the wireless keyboard sitting on my lap — I can take advantage of all the keyboard shortcuts available in my operating system and all of its applications, so it's a much more efficient means of navigating the screen. In this case, keyboard access is a necessity for different users who actually don't have disabilities, but they're using their computer in a different context that you might normally think of.
And of course, sometimes keyboard access isn't really a matter of necessity, but for some users it's actually a matter of preference. Any developer out there who is a user of Emacs or Vi knows how quickly all the keyboard bindings that are available on those editors make it fast and easy to edit your code. So again, these aren't users with necessarily any sort of disabilities, perhaps, but they just actually prefer using the keyboard. So giving your users of your particular applications the option of using the keyboard is definitely something that you want to take advantage of.
I hope at this point that I've convinced you of the importance of providing keyboard access, so now we're going to move into some of the fundamentals that are required for providing keyboard access for your widgets within the scope of the browser. First and foremost, it's important to consider what elements in the DOM fire key-related events. In HTML 4, that is actually a limited subset of DOM elements, as outlined in the specification, and those are listed up here for you. These elements, when they have focus, will fire key events: keydown, keyup, keypress, and you can listen to those events. But you can extend that out to really any element in the DOM you want using the tabIndex attribute. This allows you to make any element in HTML focusable.
The tabIndex actually has several different values that it can accept. If you set the tabIndex of your element to -1, that element is actually not in the default tab flow so you can't access it via the tab key, but you can focus it via script, and then once that element is focused it will fire key-related events. You can also give your element an implicit tabIndex based on its position in the DOM by setting the tabIndex to a value of 0, so depending on its ordinal position from the DOM hierarchy, that's the tabIndex that it will have. Finally, if you want to actually indicate an explicit tabIndex for your element, you can do so also with the tabIndex attribute. In this case, we're going to make this element the very first element in the default tab flow by setting the tabIndex to a value of 1. Those are some of the very fundamentals of key-related events — which elements they fire on, and how you make something able to fire key events.
Now that we have that core foundation and we're all on the same page, of course we want to listen for these key events so that we can provide keyboard access for our widgets. Let's take a standard use case: we've created a DHTML dialog and we want to give some users the ability to hide that dialog via the keyboard. One very common pattern is, of course, to press the escape key to hide something that's visible, and here is some standard DOM event handling code that would facilitate that type of user interaction. In this case, we're listening for the keydown event on our node that represents the root of our dialog, and when the user presses the escape key we're going to do that check via checking the key code, and then we're going to call our dialog's hide method.
So it's fairly simple, not a lot of code, but one of the things we've done in YUI 3 is actually make this a lot simpler. In YUI 3 we can actually boil that all down to one line of code very easily. Let's deconstruct this one line a little bit further, and talk about how this has made it easier with YUI 3. YUI 3 provides a single interface for all key events through a key custom event called 'key'. If you want to take advantage of that, if you want to listen for those key related events, you can do so via Y.on. Pass 'key' as the type of event you want to listen for, followed by your event handler, and then the element that you want to listen for that event on, or the node instance. Finally, you'll provide a string that represents both the event type you want to listen for, followed by any additional key codes you want to listen for, and any modifier keys.
Let's actually drill into that specification a little bit further and talk about that in greater detail. When you're using this specification, the first thing you want to decide is what type of event you're going to be listening for: keydown, keyup, keypress? Once you make that decision you can just omit that key prefix. We've settled on keydown for the sake of this example, so we're going to start off with that. Then you add a colon, and then you include the keycode that represents the key you want to listen for. In this case, escape is keycode 27, so I'm going to follow that colon with a 27. But I can actually add any number of additional keys that I want to listen for via comma delimited syntax, so if I want to listen for three keys, ten keys, and have them execute the same callback, it's very easy to do so. But again, for the purposes of this example, we're just interested in that one keycode 27, so I'm just going to omit those for now.
You can also add any additional modifiers by adding a plus and then the name of the modifier key that you're interested in. If you want to listen to shift, control, meta, etc, you can do that very simply by just indicating them using that syntax that's up on the slide there. Those aren't pertinent to this exact example, so I'm going to omit those again. Just as, again, a reminder, this spec is a string so we're going to pass the string as our fourth argument to Y.on, and that's going to essentially execute our event handler when the user presses the escape key.
But if we think back to our original example, this is a dialog widget instance that we want to hide, so what we want to do is make sure that we can call that dialog's hide method in response to that event. We're actually going to use hide as our event handler in this instance. To make sure that hide gets called within the context of that dialog widget, we're actually going to pass the dialog widget as the next argument to on, and that essentially controls what default context that this handler's going to be executed with. That essentially allows us to ensure that when hide gets called, hide is going to be executed with context as the dialog. That's how it all comes together, and that's how it is very simple to bind keystrokes to your widgets in YUI 3. We were trying to take something and make it as easy as possible for you.
The focus and blur events are two events that are incredibly useful when it comes to implementing accessibility features for your application. I'm going to walk you through several different use cases on why you would use focus and blur to help improve the accessibility of a given application.
One use case might be that you have a sophisticated application such as a mail client and you can pop up modal dialogs to inform the user of something, and you want to be able to enforce that modality. That's really easy to do if you don't think about the keyboard, because it's really easy to drop essentially an overlay that is z-ordered between your dialog and the underlying application that intercepts all mouse events to enforce that modality. But again, because we want to facilitate true modality regardless of how the user is interacting with your application, either via the mouse or the keyboard, we want to make sure that the user can't use the keyboard to send focus from this dialog back to the underlying application. We want to really enforce that modality.
We would use the focus event, in this case, to enforce that. If we were going to implement that code, what we would have to do in standard DOM is when we show this dialog, iterate all the other elements in the DOM, bind focus events to them, and then that focus listener would know to send focus back to our dialog widget. The problem with that, of course, is that depending on the number of elements we're using to actually compose our user interface, that could actually be a fairly expensive operation. We want to essentially make performance of critical concern to everybody when it comes to the browser space, so how can we provide the functionality that we need in a way that's also performant? The fact that the focus event doesn't bubble is an unfortunate implementation detail in standard DOM that we've been able to solve in YUI 3 by making both the focus and blur events bubble. So when you subscribe to focus and blur using YUI 3, you'll normally do so as with any other event. The advantage here, of course, is that we make it work better for you. With these events, when you add a focus or blur event handler to your given node instance, you're going to be listening for that event on that node, as well as its descendant nodes.
This makes it really easy to implement or enforce modality on our dialog, if we revisit that example. In this case, when we show that dialog, we're going to instrument some code like this where we use… Actually, I have a syntax error here, that should be Y.one. So we're going to get a reference to the top level document node in the DOM, we're going to bind a single focus event listener to that top level element, and that allows us to listen for focus in the scope of the entire document. We're going to call our enforce modality event handler, and what this is essentially going to do is look at the target of the event and then use node's contains method to check if the target of this focus event is within the scope of our dialog. If it's not, then we're going to send focus back to the dialog widget itself. So that's what this code is doing. In very few lines of code, you're able to implement some functionality that truly enforces modality in a way that's both friendly to the keyboard and the mouse, so that both users, regardless of how they interact with your application, know that this dialog is truly modal.
So that's one use case for why focus and blur are very useful within the scope of the browser. Another is styling focus. In this case I have designed a toolbar widget, and I have of course used CSS to style this per the style of my application. One of the things I'll notice once I load this toolbar into various browsers is that each browser draws focus slightly differently. Safari, for example, has this nice warm blue glow that's very pronounced, whereas Firefox has a much more subtle indication of focus — this tiny dotted line that surrounds just the text of our button. So since I've already gone to the trouble of skinning my application, or even a piece of my application such as this toolbar, to fit my unique design requirements for my application, I'm probably going to want to provide a rendering of focus that is also consistent within the scope of my application as well. So rather than having a focus model that is different per browser, I can converge on a single style of focus that is rendered consistently across operating systems and cross browser.
That's the design goal that I have in mind, but how am I going to accomplish that? Let's think about this. As a developer, my natural inclination is to try and leverage the focus pseudo class made available in CSS. This, unfortunately, has a couple of shortcomings to it. First and foremost is that Internet Explorer only implements the focus pseudo class on anchor elements, and even more unfortunate, this is still the case in Internet Explorer 8. The second unfortunate shortcoming of focus is that you can't easily target your ancestors. This is pertinent to my toolbar example, because the way I've constructed the markup for this toolbar is as follows: each of those toolbar buttons is actually an HTML input element under the hood. I've done that for accessibility — I want to make sure that the user knows that this is actually a button. I've styled that by wrapping that button with some spans I've used to render the rounded corners. If I'm relying on the focus pseudo class, when the input element gets focused I'm only going to have a hook to styling that element itself, when what I really want to do is apply some unique styling to the element that is the root styling container for those buttons, that outermost span with the class tb-button.
So that's my problem statement, and this problem typically leads developers down this path as an alternative. You'll end up using something like the anchor tag specifying an HREF of pound, or even worse, something like JavaScript colon void passing in 0, and it'll add a class button so that they are able to style the button the way they want and can take advantage of the focus pseudo class. This, as it turns out, provides you the rendering you want, but is really a bad design pattern for accessibility because when that anchor receives focus, first and foremost, the user of a screen reader who can't see the rendering of that button is going to perceive it as its native markup, as an anchor element. So they can have different expectations as to how to interact with that element. They're going to think that's a link. Further, a lot of screen readers are actually going to read the code that's in that anchor's HREF attribute. In this case, they're going to hear all this JavaScript code and it's not going to be in any way meaningful to them. So knowing what to do with this control when it receives focus is going to be somewhat of a disaster for users of screen readers. This is not the pattern that we want to follow when implementing a control like that.
Thankfully we have a focus event and a blur event in YUI 3 that bubble, which allows us to take advantage of another sugar method that's provided via the event utility which is this delegate method. Delegate essentially is an easy means of doing event delegation in the browser. What delegate allows you to do is defer the call to your event handler until the target of that event matches an element that is equal to the CSS selector that you specify. In this case, we want to style that outermost span that's marked with that tb-button class, so we're going to set up two delegated focus and blur event listeners on the outermost div that is responsible for holding all the buttons in our toolbar. We have one event listener of each type, so we're considering performance from the very outset. We're going to call delegate, we're going to pass in tb-button as our CSS selector, and what that will essentially do is make sure that when our event handler is called that the default context of our event in the scope of our handler is referencing that outermost span. Then we can use DOM's toggle class method to easily toggle a CSS class name that we can use to style focus when that element receives focus. So in very, very few lines of code, we can create a performant way of styling a focus that gives us a consistent rendering of focus, cross operating system, cross browser.
Another use case for styling focus might be, if we revisit this concept of a dialog, if the browser is going to render a different element with a different focus state. In this case we've got different elements of different types and we want to provide different renderings of focus depending on the different element types. We might, for the purposes of our application, want to style focus on textboxes with just a subtle blue outline. Then within the scope of our application, we might want to style buttons not with the warm glow but just with a more subtle blue shading. So depending on the type of control that gets focused, we might want to provide a different style of focus indication. We can also leverage delegate, of course, to do that very same thing. We have our dialog node, the node that represents the root node of our dialog, and we're going to use delegate to only call our style button function when the target of that focus event is an input element of type submit. That way we can provide unique styling to discrete parts of the UI controls that we want to render focus for.
So far we've talked about how YUI 3 makes it easier to add support for the keyboard, to render focus, and also to work with the focus and blur events. Another obstacle you're going to hit is that you're probably not going to have just one UI control for your page — you're going to have a page that is composed of many UI controls such as this one, where we've got both a tab control, a couple of tool bars, a treeview, a data table. Because we are interested in accessibility, we want to provide accessibility features to all these controls — we want all these controls to have keyboard access, we want all these controls to provide the correct focus style feedback as the user's navigating via the keyboard. But because we're also concerned about performance we want to do that in a way that is as performant as possible. The challenge becomes how to be both feature rich and as light as possible at the same time. As it turns out, the focus event is one way to actually strike that balance, and I'll talk about why.
There are some nice advantages to the focus event. One of its advantages is that it's fired in response to both the mouse and the keyboard. When you're using the keyboard to navigate your widget, you're using the tab key to move focus amongst all the discrete focusable portions, or UI control. Or if you're using the mouse you can mousedown on that control and that control now has focus.
The nice thing about focus, too, is it typically happens in response to very deliberate user interactions. When I mousedown on something that's a very, very deliberate action, unlike mousing over or mousing out, which is potentially a lot more of a lazy interaction; I might just be moving the mouse around the page. So if I'm a developer and I want to be loading the accessibility features of a given widget based on when the user's actually interacting with it, I want to know that the user is actually truly interested in something as opposed to they just might be interested in it. So focus is a great entry point for me as a developer to know that the user is actually interacting with this UI control, and that I should actually load all of its features at this time.
The other really cool thing about focus is that it's fired before a lot of other important events that you, as a developer, are going to be interested in, such as click, keydown, keyup, and keypress. Because it occurs before these events, we can take advantage of focus as a way of doing late binding for other event handlers that we're going to require for our widget. If we're creating toolbars, for example, it's very likely that we're going to want to add click event handlers to each of those buttons so that when the user clicks them there's going to be some sort of callback executed that handles some of our application logic. Rather than wire up all those click events in advance, what we can do is actually defer the wiring up of all the click interactions for a given widget until that widget actually has focus.
YUI 3 makes it really easy to do something like that because when you subscribe to an event via Y.on, what you get back is this handle object, and the handle has a detach method on it that allows you to detach that event handler really easily. In this case, I'm going to wire up a focus event that's going to add some additional event listeners to a given element in the DOM, but I only want that to execute the first time that element has focus, and I can use the detach handler to do that. In this case, when this first node in the document receives focus, I'm going to initialize the remaining event handlers that it needs and then I'm going to detach this event handler so that this code executes only once. YUI 3 makes it really easy to do late binding.
The other thing you can do is take this to the next level… Actually, I'm going to take questions at the very end, if you don't mind. The other thing you can do is rather than just defer the event handlers, you can actually defer the entire instantiation of a widget if you think about it. You could have the toolbar library on the page; let's say you've created a toolbar widget that has more advanced functionality to it. What you could do is essentially paint the view for that widget when you send down the initial HTML, watch for focus as it's happening in scope of the browser, and when an element that is a descendant of that toolbar receives focus — when that first focus event happens — then you can actually initialize your toolbar, render it, and then detach your event handler. It's only at that point that your toolbar widget is fully initialized, when the user is actually interacting with it.
Then you could take it even another step beyond that and not even import any of the library code you need for a given widget until that widget is actually going to be used by the user. This is particularly useful for widgets like menus, or dialogs — something that's completely hidden from the user at first glance, and it's not until the user clicks on something in the user interface that some additional components are drawn on the screen. So if I want to actually defer not only the creation of my dialog but the actual importing of the library code for my application that's actually responsible for creating that dialog widget, I can do that all using this detach method that I get back from my subscription to an event via on.
In this case, within the scope of my click event handler, what I'm going to do is call use, specify the unique YUI 3 module that I've created for my dialog, instantiate my dialog, render my dialog, and show it, and then that's only going to happen the first time. That new library code is only going to be imported into my YUI sandbox the first time I click that button, and then subsequent times that library code will be there. So it allows you to do a lot of really nice… It gives you a lot of power insofar as being able to give you the feature set you need, but also in a performant way. So it gives that good balance between accessibility and performance.
The third thing I want to talk about to you today is ARIA support. We've talked about how YUI 3 makes it a lot easier to work with the keyboard, as well as to work with focus and blur, and YUI 3 has also made it a lot easier to work with ARIA. If you're not familiar with ARIA, it's a W3C specification that not only gives you, as the developer, additional tools to further enhance the accessibility of your user interfaces, but also provide you with a lot of design patterns that help guide your development in terms of how to implement things like keyboard shortcuts. Let's talk about this in a little greater detail.
One of the problems ARIA solves is the problem of perception. When I'm authoring a widget in DHTML, I'm usually composing a widget out of a lot of different elements that already exist in HTML. In this case, I've created this TabView widget and under the hood this is based on an ordered list, and a bunch of divs, and some anchors, and I've been able to stylize it in such a way that to the user, this is able to be actually perceived as one atomic widget. My ability to perceive it as an atomic widget actually influences my perception of this thing's ability. Now that I actually see this as a TabView, I know, for example, that when I click on one of those tabs, it's going to load the content for that corresponding tab. The fact that I can see this is a TabView means I know that there are certain keyboard shortcuts that go along with any kind of basic TabView and sense that I can take advantage of. So the fact that I can perceive this as a TabView greatly influences my expectations of this widget's abilities.
That is not always the case, though. If I can't perceive this as a TabView — for example, if I'm a user of a screen reader, I don't perceive this as a TabView — what I actually perceive it as is actually the HTML elements that it's composed of. I'll play you a quick bit of audio that will further drive home the point that this is how a user of a screen reader, for example, perceives this DHTML widget.
Electronic voice: Clickable list with five items, click on the top solid link. Outer list with ten items, click: "Reid says health care bill to have public option (AP)”, link.
Todd: As you can hear, that's the screen reader moving through the different focusable pieces of this TabView that I've created in DHTML. You'll notice that the screen reader announces lists and anchors. I've done a good job of trying to make this widget with most semantic markup possible, but the user of a screen reader will still perceive that markup as its native markup as opposed to this being one atomic widget. So rather than users of a screen reader "seeing” this, this is what users of a screen reader sees. This is really important because if this is what their perception of that widget is, this therefore influences their expectations of this thing's abilities. Even though I, as the developer, have gone to the trouble of adding additional keyboard support for this widget, for example, a user of a screen reader doesn't know that this thing is a TabView so they wouldn't necessarily expect to be able to use the keyboard access that I've build into my widget. So perception is huge — you want to be able to close that perception gap as the developer. You want to ensure that all your users perceive the widgets you're designing as the widgets they actually are, as opposed to the HTML elements that they're composed of.
ARIA helps close that perception gap. What ARIA allows you to do is overlay some additional semantics on top of your existing HTML so that you can control the perception of that widget for users of screen readers, for example, so that when they focus into this widget they don't actually perceive it as a set of lists and anchors, they actually perceive it as a TabView. Here's a quick demo of how this TabView would be perceived by a user of a screen reader.
Electronic voice: Today's news. Press the enter key to load the content of each tab: tab control. 'World' tab selected, two of five. 'Entertainment' tab selected, three of five. 'Entertainment' property page list. Out of this list with ten items clickable: "Michael Jackson's 'This Is It' to Open Worldwide (AP)”.
Todd: As you can hear, now when the user is actually interacting with this element they hear the tabs — they hear when they've gone from the selected tab into the main content area, and that they've now entered the tab's corresponding panel. They know that this is a TabView, and that perception influences their expectations. As a screen reader user, when I first tab into that tab set and I know that I'm in tab one of five, I know that I can use the arrow keys to move amongst those different tabs. So my perception influences my expectations of this widget's abilities, and ARIA's ability to help you close that perception gap with your users is really important. That's one of the roles that ARIA plays in accessibility.
The way you add ARIA roles and states to your markup is through the setAttribute interface. For example, in native DOM, if I want to add a role of tab to a given element in my TabView, I would do so through setAttribute. There are also corresponding ARIA states that represent different states of a given widget, and you'd set those also through setAttribute. States are always prefixed with that ARIA dash prefix.
We have authored node in such a way that node knows about ARIA, so this gives you a little more terse syntax for adding roles and states to a given element. It also allows you to use the setAttributes method if you want to set multiple roles and states together in all in go. So, for example, if I know a given tab is going to be disabled by default until some other thing changes within the scope of my application, I can do that with just one call to setAttrs.
But that's not the only benefit that having node know about ARIA provides. The fact that node knows about ARIA makes it really easy to set the roles and states in a way that's really, really easy, because typically you're not going to be applying one role, you're going to be applying a role to multiple elements throughout a given widget's subtree. In the case of our TabView, in the markup that is used to represent this TabView each tab is represented by an anchor element whose HREF points at the ID of its corresponding tab panel, and that's our underlying semantic markup for this thing. All of our tabs are based on this natively focusable anchor element because of some sort of core foundational keyboard support.
Then, of course, we have multiple tabs and multiple corresponding tab panels, and what we want to do is apply all our role of tabs to the anchor elements and all the roles of tab panels to our corresponding divs with the class tab panel. We can use the power of nodes built in CSS selector with the fact that node knows about ARIA to do this very efficiently in two lines of code. In our first line of code we get a reference to our TabView, and we call all to get a collection of all the nodes that match that specified CSS selector. We're going to gather up all the anchor elements that represent our tabs, set the role attribute to tab, and similarly, we're going to set the role of all the elements with the class of tabpanel to a role of tabpanel. It just makes it a little more efficient for both the application and management of the ARIA roles and states.
Beyond offering additional semantics for you, as a developer, to take advantage of, ARIA also offers some guidance with respect to design patterns for keyboard interaction that make widgets even easier to use for users who are navigating via the keyboard. If we revisit this toolbar that we talked about a little bit earlier, because each of these buttons in this toolbar is based on an input element, each one of these elements in this toolbar are in the default tab flow. So if I want to navigate amongst all the buttons in this toolbar I can easily do that by pressing the tab key, and then I'll get focus feedback accordingly. This works really well for navigating amongst the focusable elements of my toolbar, but it's likely that I'm not just going to have one toolbar on the page. It's likely that I'm going to have many toolbars. For example, if I'm building a rich text editor, I've got several different toolbars that compose this widget.
One very common thing I might want to do is… I might be starting at a point where I'm focused on this bold button in the font cell toolbar, and I want to be able to navigate via the keyboard over to the insert item button which is several keystrokes away from me. In order to move to that new button, to go from point A to point B, I actually have to pass through several different active toolbars which will cost me a good number of keystrokes — I think if I tally them up here, of all the buttons that are actually active, it'll cost me 11 keystrokes with the tab key. So there's actually a better way to accomplish this, and ARIA provides some guidance with respect to this.
For widgets that manage descendants of similar types, such as all the buttons in a toolbar, rather than have all those buttons in a default tab flow we can limit the number of elements in the tab flow to just one. That gives you two things: one, users can move into and out of your user interface control really quickly using the TabView, so they can navigate across widgets really efficiently. Then once they tab into a widget they're interested in, they can therefore use the arrow keys to move focus amongst the descendants of that control. If we implement that pattern here, that allows us to quickly use the tab key to cut the number of keystrokes in half, going from our start point to our end point — that insert item button that's highlighted in blue — in just four presses of the tab key.
If we zoom back into our toolbar example, again, by default all these buttons are going to be in the default tab flow. But we want to do this, we want to make it so that just the first button in the toolbar is focusable and the rest have a tab index of -1, so they are not in the default tab flow. Users can tab into and out of this toolbar really efficiently, and once they have focused into a toolbar, if this is the control they're actually interested in working with, they can use the arrow keys to navigate focus between all the elements, or the descendants of this toolbar, really easily. This allows for efficient focus management within a widget, and efficient navigation amongst widgets on a page, using a TabView. So this is the design pattern that ARIA wants you to implement for your widgets that manage a collection of descendants.
We recognize this is a valuable pattern, so we've tried to provide a utility that makes working with this pattern really easy in YUI 3. That utility is called the Node Focus Manager. Like every other component in YUI 3, you would use it via the use statement, so in this case I'm going to specify node-focusmanager via use. Then I'm going to get my reference to my toolbar node via Y.one, then all plugins you use through the plug statement. So I'm going to call plug, I'm going to pass in a reference to my node focus manager plugin, and that's going to now augment the abilities of this node with some new abilities to manage the focus of its descendants.
The second argument to my plugin is always going to be an object literal that allows me to set up some properties of this toolbar, and the first meaningful property for this particular node focus manager plugin to talk about is the descendant's property. The descendant's property allows you to control which descendants of this node you want to manage the focus of. In this case, for the toolbar, it's going to be all the buttons in the toolbar. Descendant accepts a CSS selector, so it allows you to really easily target the elements that you're interested in managing. In the case of our toolbar, we want to manage just input elements of type button, so that's going to be our CSS selector, and then the second configuration property for the node focus manager is going to be this keys configuration property.
Keys has two sub-attributes, next and previous, and these attributes essentially allow you to specify which keys you want to use to navigate to the next and previous focusable descendants of this node. Both of these attributes take the key event syntax that we were talking about earlier in the talk. In this case, I want to be able to move to the next button by pressing the right arrow, and I want to move to the previous button in this toolbar by pressing the left arrow. That's the key syntax for doing that. So very simply and in just a few lines of code, I can then implement that ARIA pattern for managing focus amongst widget descendants using the node focus manager plugin.
Just for review, what that code will do in the scope of our toolbar is it will reduce the number of elements that are in the default tab flow down from six to just the first button. Then the user can use the arrow keys to move — right and left arrow will move the focus amongst those buttons. This is really good because it transforms widgets into something that are both… It's not just a keyboard model that works, but it's actually more usable because it allows the user the opportunity to move between widgets really fast, using the tab key. There's a big difference between something being workable and usable, and that's what we're really interested in doing when it comes to accessibility — improving the usability of a widget.
Focus manager has some additional sugar properties that you'll probably be interested in. Not only are you going to want to move focus amongst those buttons in your toolbar, you're probably going to want to style focus like we talked about earlier. You can do that really simply via the focus class property. In this case, I'm going to set focusClass to this CSS class name 'yui-button-focus', and when each button in that toolbar gets focused, it's going to have that class name applied to it. That gives me an easy way to style focus on each button as it's focused. Then there's an additional property called circular, and that essentially allows the focus manager to cycle back to the beginning of the list of descendants if I'm at the end, or vice versa. So if I reach the last button in my toolbar and I push the right arrow key, it's going to cycle me back to the first button. It's just a nice affordance for users of the keyboard to be able to able to navigate focus amongst a widget's descendants more efficiently.
There are a lot of use cases for the focus manager node plugin beyond the toolbar. Anything that has a set of like descendants, really, the focus manager's a great candidate for. For example, all the tabs in a TabView would have this exact same behavior. I'd be able to move focus into and out of this TabView really easily by just tabbing that first tab and then tabbing out of it, and then once I'm in that TabView, if I want to actually navigate amongst the tabs I could use the arrow keys. Very similarly, with any kind of a pop up menu, I can move focus up and down the menu items via the up and down arrow. Again, focus manager makes it really easy to implement that style of user interaction.
It's worth pointing out that if you, as a developer, want guidance on all the different keyboard shortcuts that you should be implementing for a given DHTML widget that you're creating, the ARIA specification has a corresponding best practices document that provides a lot of great detail on how these keyboard shortcuts should be implemented. It's also worth mentioning that this pattern of keyboard user interaction is not something that's unique to ARIA, or unique to YUI 3 within the scope of this node focus manager. These are just general best practices that have existed for a long time in the desktop, and that we're now able to bring to the browser. So that's what you'll find provided in detail through the ARIA best practices document.
The last thing I wanted to leave you with today is that I hope you'll understand that one of our goals with YUI 3 was to give you the tools as developers to develop widgets that are really accessible. The general goal is to make it just as easy to do your own implementations of a given widget as it is easy to consume pre-canned implementations of widgets that we've already given you. So if you're rolling your own widget and you want to implement accessibility features, it's just as easy to do so as it would be to consume one of our existing widgets. We want to give you the tools that empower you, just as we're going to use these tools under the hood when developing our own widgets.
That's the conclusion of my talk. That's my virtual business card with my contact information, so if you want to email me or follow me on Twitter, that's the information that you need. Here is some quick attribution for images within the scope of my presentation. And that's it. If there are any questions I'd be happy to take those now, but otherwise, thank you so much for coming.
[applause]
Tue, 09 Feb 2010
YUI Theater — Douglas Crockford: “Crockford on JavaScript — Volume 1: The Early Years”
Wed, 03 Feb 2010
YUI Theater — John Resig: “Testing, Performance Analysis, and jQuery 1.4″
Wed, 16 Dec 2009
YUI Theater — Todd Kloots: “Building Accessible Widgets with YUI 3″
Mon, 23 Nov 2009
YUI Theater — Isaac Schlueter: “Solving Problems with YUI 3″
Fri, 20 Nov 2009