JavaScript interaction must be input device independent

Time for a bit of what in my opinion is Accessibility 101: input device independency. For some reason it is common to see otherwise nice uses of JavaScript made inaccessible by developers forgetting that not everybody uses a mouse (or some other pointing device).

There are several groups of people who do not use a mouse or pointing device to interact with Web pages. A few examples:

  • Mobility impaired people who cannot use a mouse at all
  • People with motor impairments who can use a mouse but lack fine motor control
  • Screen reader users who do not use a mouse, or even a monitor
  • People using mobile phones
  • Laptop users, since most laptops have really bad trackpads or other means of positioning the cursor (ever tried using a hierarchical dropdown menu with a trackpad while riding on a train?)
  • Speed typers who have learned to use keyboard navigation efficiently and are slowed down when they have to switch to their mouse (if they have one)

As you can see there are reasons that affect people with disabilities, and others that affect everybody. So when developing a user interface you have to make sure that it does not depend on a particular input device. See also WCAG 1.0 Guideline 9. Design for device-independence.

The most common problems I see are drag-and-drop functionality and onclick event handlers added to arbitrary elements. For drag-and-drop, you need to give users an alternative way of interacting. How to do that is up to you and is not something I am going to go into here.

For onclick event handlers, the solution is simple. Always use an a element with a value assigned to its href attribute. In some cases a form control is a better choice, but I’ll use links here.

Doing so makes the functionality accessible to keyboard users and other people who do not use a mouse. And yes, you really need to consider those users. I was very surprised to see several people argue against supporting non-mouse users in Jonathan Snook’s SnookSurvey #2: To Link or Not?. I don’t buy any of their arguments, which mostly seem based on a misunderstanding of the problem.

I’ll illustrate the onclick case with a couple of code examples consisting of a list of terms and definitions, where the definitions are hidden until the user clicks on a term. The examples use inline event handlers, style attributes, and excessive class attributes for clarity – do not use this code.

The wrong way

Here is how to do this the inaccessible way:

  1. <dl class="faq">
  2. <dt onclick="showDefinition('def1')">Term 1</dt>
  3. <dd class="def1" style="display:none">Definition 1</dd>
  4. <dt onclick="showDefinition('def2')">Term 2</dt>
  5. <dd class="def2" style="display:none">Definition 2a</dd>
  6. <dd class="def2" style="display:none">Definition 2b</dd>
  7. </dl>

The dd elements are hidden when the page loads (if CSS is enabled). When the user clicks on a dt element, the showDefinition() function changes the display property for the corresponding dd elements, making them visible. You could also use CSS to change the cursor property when the user places the cursor on top of the dt elements as a way to indicate that they are clickable. I don’t personally like doing so since it is very confusing when JavaScript is unavailable – it looks like you can click the text since the cursor changes, but when you click nothing actually happens.

This works fine for people who use a mouse. But what if you don’t? When you tab through the page, focus is placed on links and form controls, allowing you to interact with them. But focus is not placed on other elements, even if they have an event handler assigned to them. Because of that, it is impossible for non-mouse users to interact with the page. Bad, bad, bad.

Note: Using CSS to hide an element and JavaScript to show it creates accessibility problems since people with CSS on and JavaScript off will not be able to display the hidden elements. The way around this problem is to use JavaScript to hide the elements as well. I am doing it the bad way here since it makes it easier to follow the examples.

The better way

The simple solution is to use links. All browsing devices can follow links, and if they support JavaScript, activating a link will trigger an onclick event. With that in mind, here is the markup that is keyboard friendly (again, inline stuff to show what I mean - imagine the links and event handlers having been inserted by a script):

  1. <dl class="faq">
  2. <dt><a href="#" onclick="showDefinition('def1')">Term 1</a></dt>
  3. <dd class="def1" style="display:none">Definition 1</dd>
  4. <dt><a href="#" onclick="showDefinition('def2')">Term 2</a></dt>
  5. <dd class="def2" style="display:none">Definition 2a</dd>
  6. <dd class="def2" style="display:none">Definition 2b</dd>
  7. </dl>

A JavaScript function runs when the page (or the DOM) is loaded, inserts the links, and cancels their default behaviour to prevent them from navigating to the URL in the href attribute. The links only trigger the showDefinition() function.

Note that neither of these examples are complete solutions. They are just meant to illustrate the different approaches. In a production situation the links would be added with unobtrusive JavaScript.

There are usability issues to keep in mind with any approach that deviates from the normal behaviour of web pages, but I’m not going into that here. The purpose of this article is to show that you must not make website interaction depend on a certain input device, and that there is an easy way to make sure everybody can use your interface.

For a more detailed and comprehensive discussion of this subject, I recommend Accessible JavaScript: Beyond the Mouse by James Edwards.

Update: I realise that I didn’t stress enough that my examples aren’t complete. The links in the second example would not be in the raw markup but inserted by a script, as I mention in the paragraph following the code listing. The comments that mention ways of improving this are all correct :-).

Posted on August 21, 2007 in Accessibility, JavaScript

Comments

  1. August 21, 2007 by Anders Tornblad

    If you have server-side code capabilities, your href attribute values should not be simply “#”, but rather something like “?showsection=def1”, thus letting your server-side code parse the value of showsection to alter the style of the selected section.

    That way the click will work as expected even when javascript is turned off.

  2. Roger,

    Good article and good points you are making. I responded on Snook’s post with another idea regarding using tabindex to allow elements to receive focus, I’m curious what you think of this idea?

  3. August 21, 2007 by Roger Johansson (Author comment)

    Anders:

    Depending on the situation I agree that would be a nice way of handling JavaScript off.

    Jeff L:

    Once tabindex for anything is defined in a specification and works in all browsers it is an option. Right now though neither of those conditions is true, which rules it out for me.

  4. You mentioned that your examples used inline event handlers for simplicity of explaining the problem so with that in mind adding this comment to further the discussion but not to call you out on that decision.

    In real life situations it’s obviously best to avoid using inline event handlers as they mix behaviour and content. Much better would be to attach a single event handler to the definition list via an external script file and use event delegation to trigger the show/hide toggle functionality on each of the anchors.

    Assuming no server side fallback capability you could go a step further and dynamically add the links to each definition term with your JavaScript. Without JavaScript you’d then have an expanded list of definition term/description pairs and no “non-clickable” links which might confuse the user.

  5. I absolutely agree with Anders: The right way to do this is cleary unobtrusive scripting.

    I’d prefer the following:

    • Mark up the list of links that simply load the answer, just a link, no ajaxy stuff
    • Inject the answers and hide them
    • Add a trigger to the link that shows the respective answer

    But you might also want to use this, depending on the size of the FAQ etc:

    • Mark up two lists, Q&As
    • Each Q links to the respective A (via anchor)
    • JS: Hide the A list, add event that shows the A

    Let’s see what the user gets:

    Without JS:

    • Method 1: A list of Qs, if you click one, you’ll see the A
    • Method 2: A list of Qs and a list of As, if you click a Q, the A scrolls into view

    With JS, in both cases:

    • A list of Qs, click a Q, A is displayed

    I thought this was fairly straightforward and I find it somewhat surprising that e.g. Jeff Croft and many others don’t seem to agree…

  6. When using onClick for links, you should always leave the HREF intact as Anders eluded to.

    You can always use a return: false; to cancel out the HREF being called if JavaScript is enabled. This is especially true when opening onClick links in a new tab, which can be a problem if the HREF isn’t a valid URI.

  7. Anders Tornblad - I’m also keen on the idea of providing a server side fallback (particularly if the list of definition terms/descriptions) is very long when expanded. If SEO is of particular importance though one needs to careful to avoid search engine crawlers seeing the different collapsed and expanded states of the same page as duplicated content which could negatively impact search engine ranking.

  8. I’m agreed about such functionality, but hash links (#) not always can be used. For, example , if you have ajax’d web application, i guess, you use window.location.hash for “vertical” links. Also, you can’t leave empty “href” cause of validation. But this should work <a href=”/” onclick=”showDefinition(‘def1’); return false;”>Term 1</a>

  9. August 21, 2007 by Jean-Sébastien Girard

    A mildly unrelated comment, as a user interest in such issues (So I might be missing something): I’ve gotten to utterly despise the use of hash links for such Javascript. There is a very simple reason for this: in some browsers, such blank anchors targets will activate the event just fine, but will return the browser to the top of the page, which is incredibly annoying when navigating even mildly long pages.

  10. August 22, 2007 by Roger Johansson (Author comment)

    All:

    Sorry for causing a bit of confusion. I wasn’t obvious enough when I tried to explain that the links in the second example should not be in the raw markup. Instead, they would be injected by a bit of unobtrusive JavaScript (which would also cancel the default behaviour of the links to make sure that the browser does not follow them and jump to the top of the page). All that is mentioned in the paragraph following the code listing ;-).

    In some cases there would be a proper href value you could insert, but sometimes that is not needed. It all depends on the situation.

  11. August 22, 2007 by Anders Tornblad

    Ed Eliot: Wouldn’t rel="nofollow" take care of that issue, preventing (good) crawlers from seeing duplicate content?

    Jean-Sébastien GIRARD: How about using the anchor link to your advantage, then? Instead of just using href="#", use href="#ID_OF_THIS_ELEMENT". Or, as in my example, href="?showsection=def1#ID_OF_THIS_ELEMENT"

    Robert: Could you give an example of a situation where proper href values would not be possible/needed?

  12. August 22, 2007 by Anders Tornblad

    I’m tired. Roger is now called Robert, apparently. Sorry!

  13. August 22, 2007 by Roger Johansson (Author comment)

    Anders: No problem! ;-).

    I think the example I use here does not really benefit from proper href values, i.e. internal links. Linking a term to its explanation would just cause a very slight jump if the links were there in a browser with no JS. Of course, if you need to be able to link directly to an explanation from another page (still talking about this particular code example), having something other than just “#” in the href would be necessary. Just my opinion, which is by no means the only correct one.

  14. I always use both onclick and onkeypress event handlers when I need to accomplish something similar to this so it allows people not using a mouse to activate the JavaScript

  15. Anders Tornblad - sadly rel=”nofollow” isn’t particularly effective. Most crawlers will still follow the link anyway.

  16. I totally agree with Anders, Ed and Tom. The HTML should be simple and work without JavaScript or CSS. That’s good for accessibility, SEO, and maintenance. Personally, I think that hard-coded HTML links on the DT’s aren’t really necessary.

    Once the page has loaded, JS can kick in, add links, further styling, or fancy show/hide animation.

  17. Nice example Roger. I just recently had to create a FAQ, which apparently isn’t all that accessible to keyboard users.

    I my FAQ I don’t use display:none to hide the DD’s since screenreaders won’t read this. This way blind people get to ‘see’ a normal DL (which they can jump through using their screenreader shortcuts). So this solution is screenreader friendly.

    But when I want to make it accessible for keyboard users by adding a link, screenreaders will ‘see’ a link that points nowhere, and doesn’t do anything (at least not visible to them). So by making it more accessible to keyboardusers I’m making it less accessible to screenreaders.

    Do you or anyone know of a way to prevent this.

  18. I responded on Snook’s post with another idea regarding using tabindex to allow elements to receive focus, I’m curious what you think of this idea?

    The problem with that idea is that it’s not valid markup. The tabindex attribute is only valid for links and form controls, plus area and object elements. It’s not valid for headings.

  19. August 22, 2007 by Stevie D

    Beau West:

    You can always use a return: false; to cancel out the HREF being called if JavaScript is enabled. This is especially true when opening onClick links in a new tab, which can be a problem if the HREF isn’t a valid URI.

    If you’re not using a valid URI then your function is not accessible to people without Javascript activated.

  20. August 22, 2007 by Stevie D

    I am similarly disappointed in some of the responses to “To Link or Not?”. What no-one seems to have realised is that the page suggested is fundamentally inaccessible, because it requires Javascript to even think about working - and that’s before you start to consider such monstrosities as tabindex on headings.

    I tried to post a comment but it appears that Jonathan is cocking a snook at IE-users because the comments form doesn’t work in IE, so it will have to wait til I get home and back to my own computer…

    On my own website, I have a confession to make - I have a drop-down list that activates a link by Javascript onChange, making it useless to keyboard users. My justification for this is that all pages can be accessed by following two or three regular links, and this just provides a quicker way for most people to get there, and that the time it would take me to learn the programming to do it server-side would stop me from doing more important things, or would relegate the whole thing to the back-burner.

    Quick question - is there an easy way to make this link accessible to keyboard users, eg by activating the function onBlur rather than onChange, and having a dummy “Go” button for people to click or tab to to activate the link. I think that should work (having only thought about it for the first time 2 minutes ago) but would be interested in any views that other people have on doing this.

  21. August 22, 2007 by Anders Tornblad

    Stevie D: Your dropdown is probably pretty accessible as-is to keyboard-savvy users. On a Windows machine, you can use Alt-Arrow down to open the dropdown without triggering the onchange before an item has been truly selected. I’m guessing there is a similar option on the Mac.

    Unfortunately, that is not how most users do when accessing a dropdown using the keyboard. An onblur implementation would probably work, though, but at the same time, it would render the benefits for the mouse user almost to nil, because mouse users would have to click elsewhere for the effect to take action.

  22. Great topic!

    In the dojo toolkit we are providing an accessible “onDijitClick” (was “onklick”) event markup which fires on click, space, and enter key. Developers can still use the regular click for cases where there is already keyboard support.

  23. August 22, 2007 by Jean-Sébastien Girard

    Anders: I sure wish more people actually did that XD.

  24. August 23, 2007 by Gerben

    @Stevie D: You could probably add an onkeypress event handler to the drop-down, and detect when keyboardnavigation is used and disable onchange. You’d have to check if indeed the onkeypress is fired before onchange. You could then activate the ‘link’ when the enter-key is pressed.

    The only problem is users that combine keyboard and mouse navigation, but this is probably also solvable.

  25. I was very surprised to see several people argue against supporting non-mouse users

    Crikey. As a laptop user who’s not a fan of the trackpad, I’d be surprised at such arguments too.

Comments are disabled for this post (read why), but if you have spotted an error or have additional info that you think should be in this post, feel free to contact me.