Let Them Eat Cake

There has been a growing debate lately that pits accessibility against usability, but there’s no reason we can’t have both. If you can make a page more usable without making it less accessible, by all means do so; do not let your inability to translate certain usability enhancements into accessible functions or features restrict your use of those enhancements. As long as the enhancements do not restrict accessibility, go for it.

Article Continues Below

As many of you know, with JavaScript and the DOM, we have the ability to control every element on a well-structured web page. It is my feeling that, using the DOM, we can improve the usability of a page without restricting its accessibility. Sprinkle in a little CSS, and we have the recipe for a wonderful experience all around, regardless of the browser, platform or device being used.

Ingredients: a well-structured document#section2

To begin, we need a well-structured document. My example will be an article, this one in fact, consisting of a single page with jump refs to the various content sections.

Let’s establish that our goal is to present this article in a highly accessible, yet entirely usable way. The article is divided into sections and, as human psychology demonstrates, information is easier to digest in chunks. One way to make a lengthy article easier to follow is to display the content section by section. This not only aids in digesting the information but reduces scrolling (which appeals to the many who remain convinced that the general population still has no clue how to scroll).

How do we meet this usability goal without causing accessibility issues? Enter the DOM.

Preparation: pour in the DOM; mix and bake#section3

As you have probably noticed, the navigation links within the header section of the document are jump refs to the different sections (divisions) of the article. The first link (“Introduction”) takes you to the introduction of the article (wrapped in <div id="intro"></div>). This not only makes the page easier to navigate, but it also makes it easier for other people to reference particular sections of the article from their own work (via citations or links). The helpful “Back to top” links also reorient users as needed.

Glancing at the source, we can see that the whole document uses semantic ids for divvying up the document into manageable chunks. This offers us a world of possibilities. With the aid of the DOM, we can easily traverse the page, looking at the ids of the divisions, hiding the ones we don’t want immediately visible. The basic code would look something like this:

  function hideDivs()
  {
    var divs =
      document.getElementsByTagName("div");
    for(var i=0; i < divs.length; i++)
    {
      var div = divs[ i ];
      var id = div.id;
      if ((id != "header") &&
          (id != "footer"))
      {
        div.style.display = "none";
      }
    }
  }
  
  window.onload = function()
  {
    hideDivs();
  }

First, we collect all of the <div>s on the page into an array, and then we loop through the array, hiding all of them with the exception of the ones with ids of “header” and “footer”. In the interest of hiding the script from older browsers which don’t understand document.getElementsByTagName, we should add the following line to the beginning of the function:

if (!document.getElementsByTagName) {
  return null;
}

Now we have hidden everything from the browser (with the exception of the header and footer <div>s), which means that no article content is available for the user to read. We should show the introduction right off the bat, so we need to make a slight modification to the hideDivs function. We could hard code a conditional statement for id == "intro" into the function (alongside the header and footer ones), but that doesn’t give us much flexibility. If we add an argument to the function called exempt, we can specify a <div> to exclude from the function on the fly:

  function hideDivs(exempt)
  {
    if (!document.getElementsByTagName) {
      return null;
    }
    if (!exempt) exempt = "";
    var divs =
      document.getElementsByTagName("div");
    for(var i=0; i < divs.length; i++)
    {
      var div = divs[ i ];
      var id = div.id;
      if ((id != "header") &&
          (id != "footer") &&
          (id != exempt))
      {
        div.style.display = "none";
      }
    }
  }
  
  window.onload = function()
  {
    hideDivs("intro");
  }

We pass the value “intro” to the function upon page load as the exemption, and that sets up our document for its initial state. But how do we allow the user to view other sections? If we take a look at the document structure, we already have links to the varying sections as jump refs. This is perfect, because each jump ref link (e.g. #ingredients) already contains the id of the <div> we want it to show. All we have to do is find a way to change the link so that it triggers a JavaScript funtion to show the chosen section and hide the others. Enter the DOM again:

  function fixLinks()
  {
    if (!document.getElementsByTagName) {
      return null;
    }
    var anchors =
      document.getElementsByTagName("a");
    for(var i=0; i < anchors.length; i++)
    {
      var a = anchors[ i ];
      var href = a.href;
      if ((href.indexOf("#") != -1) &&
          (href.indexOf("header") == -1))
      {
        var index = href.indexOf("#") + 1;
        href = "javascript:show('" +
          href.substring(index) + "');";
        a.setAttribute("href",href);
      }
    }
  }

In this function, we parse the document looking for <a> tags which contain “#” in the href attribute. In order to avoid grabbing the “Back to top” links, we add in an exclusion of links containing the term “header”. The id of the <div> the link was referencing is separated from the #, and the href is then reconstituted as the target <div> id wrapped inside a JavaScript function called show. We add fixLinks to the list of functions called upon in the onload function, and we’re halfway there.

The brilliance of having JavaScript rewrite the link as a JavaScript call is that we already know JavaScript is operating on the user’s device, so we are assured that <a href="javascript:show(‘ingredients’);"> will not be meaningless to the browsing device. If a user’s browsing device does not support JavaScript (or does not support it to the extent that it understands document.getElementsByTagName), the links remain unchanged, jump refs to the different divisions of the page. Likewise, the divisions all remain visible, so both accessibility and usability are well-served.

Now, about that show function… This is a simple function that uses the DOM to show an identified node and hide the rest. We already have a great multi-purpose hiding function in hideDivs, so all we need is the “show” part. Here’s the code:

  function show(what)
  {
    if (!document.getElementById) {
      return null;
    }
    showWhat =
      document.getElementById(what);
    showWhat.style.display = "block";
    hideDivs(what);
  }

We begin by testing to be sure document.getElementById is supported, and then we display the <div> with an id equal to the value we passed to the function. We wrap it all up with a call to hideDivs, passing it the value of the newly shown <div> as the exemption. The resulting page only needs us to make it a little more attractive.

Decoration: liberal application of CSS#section4

This part is up to you; decorate as you see fit. My basic example has a cakey theme with minimal decoration. Apart from aesthetics, however, there are some improvements we can make to what we have done so far using CSS.

A problem with the code we’ve written becomes clear when a user goes to print the article (which I hope they do). You see, having JavaScript change the display style of a node causes most (if not all) browsers to treat the style application as an inline style. According to the cascade rules of CSS, inline has the greatest precedence, so it overrides all other styles. If we want someone to be able to print the entire article and not just one section at a time, we need to make sure that all sections of the page are visible. This is accomplished by creating a class (let’s call it “hidden”) within a stylesheet that is applied to the screen media type only, and hides the content:

  .hidden {
    display: none;
  }

In your print stylesheet (if you have one), leaving out this class would cause anything classed with it to be displayed by default.

Now we can go back and fix the JavaScript functions accordingly:

  function hideDivs(exempt)
  {
    if (!document.getElementsByTagName) {
      return null;
    }
    if (!exempt) exempt = "";
    var divs =
      document.getElementsByTagName("div");
    for(var i=0; i < divs.length; i++)
    {
      var div = divs[ i ];
      var id = div.id;
      if ((id != "header") &&
          (id != "footer") &&
          (id != exempt))
      {
        div.className = "hidden";
      }
    }
  }
  
  function fixLinks()
  {
    if (!document.getElementsByTagName) {
      return null;
    }
    var anchors =
      document.getElementsByTagName("a");
    for(var i=0; i < anchors.length; i++)
    {
      var a = anchors[ i ];
      var href = a.href;
      if ((href.indexOf("#") != -1) &&
          (href.indexOf("header") == -1))
      {
        var index = href.indexOf("#") + 1;
        href = "javascript:show('" +
          href.substring(index) + "');";
        a.setAttribute("href",href);
      }
    }
  }
  
  function show(what)
  {
    if (!document.getElementById) {
      return null;
    }
    showWhat =
      document.getElementById(what);
    showWhat.className = "";
    hideDivs(what);
  }
  
  window.onload = function()
  {
    hideDivs("intro");
    fixLinks();
  }

One final housekeeping note: we should re-examine those “Back to top” links. Now that we have a page which only shows sections based upon the link we click, we simply don’t need them around, confusing people. Additionally, we don’t want them popping up in a print version of the page, where they really don’t serve any function. One quick addition to the fixLinks function can solve this:

  function fixLinks()
  {
    if (!document.getElementsByTagName) {
      return null;
    }
    var anchors =
      document.getElementsByTagName("a");
    for(var i=0; i < anchors.length; i++)
    {
      var a = anchors[ i ];
      var href = a.href;
      if (href.indexOf("#header") != -1) {
        a.className = "alt";
      } else if ((href.indexOf("#") != -1) &&
        (href.indexOf("header") == -1))
      {
        var index = href.indexOf("#") + 1;
        href = "javascript:show('" +
          href.substring(index) + "');";
        a.setAttribute("href",href);
      }
    }
  }

With the addition of an .alt style to the screen and print stylesheets…

  .alt {
    display: none;
  }

...it all comes together nicely. I encourage you to explore print stylesheets further by reading Eric Meyer’s superb reference on the subject, CSS Design: Going to Print.

Suggested serving: one document, infinite possibilities#section5

This article, though by no means meant to be the end-all, be-all on the concept of helping usability and accessibility play nicely together, will hopefully give you inspiration and methods to improve your own projects, in order to meet diverse needs, while excluding no one. As accessibility becomes a greater concern in the private sector, we need to keep usability fresh in our minds as well. The examples created here are meant to give you ideas for rewriting your usability enhancements with accessibility in mind and designing accessible pages with usability in mind. There are infinite possibilities out there… bake your own and enjoy.

85 Reader Comments

  1. I was wondering if there was any easy modification that could be done to the script to make it just toggle the div clicked on from display:none to display block back and forth instead of just showing the one clicked on and collapsing all others? Sorry my scriping is horrible.

    I’m making a program that will ideally generate a html list of the files on a person’s hard drive in a directory tree. when you click on a node of the tree, it will reveal the files in that node and their file information in a table. If you click on a child node with this kind of script, it will collapse the parent div when hidedivs is called.

    All of the other scripts i’ve been able to find for toggling don’t include a function to collapse all nodes on page load, so i was trying to use this one for cross platform compatability without having to put inline display:”none” everywehre.

  2. Everything seems to be very smart but pressing the back button to get back to the previos section (Introduction, Ingredients…) you “jump” back to ALA site.
    The document behaves in an unexpected way, this could create confusion and disapoint among users (the same it happes with Flash sites)

  3. Speaking of usability and accessibility, wouldn’t a good practice be titling one’s articles with something a bit more descriptive than “Let Them Eat Cake,” especially when said article titles appear in syndicated RSS feeds?

  4. Earlier in the discussion, Dante-Cubed introduced a link to his DOM-based AccessKeys method, as a better example of adding accessibility and usability. Is this to suggest that AccessKeys isnt as useless for accessibility as I’d beleived until now for the reasons decribed at http://www.wats.ca/articles/accesskeys/19 ? “So while it seems that Accesskeys is a great idea in principle, implementation brings with it the possibility that it either will not be available to all users, or that the keystroke combination encoded within the web page may conflict with a reserved keystroke combination in an adaptive technology or future user agent.”

  5. in one hand i agree the arguments about ’emulating a browser is bad’….and that this trick is only good for cool visual affects like collapsing the content of a long article…

    But with all the arguments about ‘if my browser doesn’t support javascript’ all i can say is …. GIVE ME A BREAK!!!!!

    come on, trash your PREHISTORICAL browser and Download a brand new SHINY one.

    Who can be creative without considering the best tools available …. and i’m not talking about ‘HIGH END GEEK TOOL’…i’m talking about THE WELL KNOW JAVASCRIPT…

    a good article … with a bad example… but tools exists just to serve your imagination… if you don’t have imagination…STOP READING THIS BLOG….no?

  6. Hello all,

    I am really amazed and thrilled at the amount of discussion this article has generated. And I am please with all of the comments (even the ones which paint me as out of my mind — perhaps because I’m nearly there). Anyway, just some quick thoughts, responses and background.

    First off, this article is less about the tabbed viewing (in retrospect, perhaps a poor example, but one I figured everyone could easily grasp) and more about the power of using JavaScript only when you know it will be supported. Some people got this right off the bat, others… not so much, but anyway, that was the concept. It was meant to get you thinking, which many of you have.

    Dante-cubed has come back to this again and again with some great ideas, as has Eric L. I really like the idea of implementing a “show all” as stylo suggested. But Vinnie Garcia is right in looking forward toward CSS3 support. Once this is supported, using the :target selector, there would be no need for the inline JS Sam suggested and Eric L implemented. But, being that full CSS3 support is a long way off (at least in the Windows world as IE will now be inextricably tied to the OS), I would recommend taking Sam’s suggestion, but wrapping it in a function which would write out the onclick behavior on the fly (and only if javascript is enabled).

    Anyone who has problems with my JS can feel free to gripe all they want, I am certainly no master. Keep in mind, however that these examples were meant to be readable by everyone (even my wife, who has no clue what I do). Think of it as code on a 4th grade reading level — the USA Today of the JavaScript world. I think using behaviors assigned by JS is great, but wanted this to be very easy to follow.

    As for those of you who found the errant CSS3 text-shadow, that was a wink to the Safari community (which I am not a member of, being PC-based). I realized support for it wasn’t good, but threw it in as a dream (of sorts) that some day browsers would be up on their CSS support.

    Thank you all for reading and keep adding to the discussion. I am really glad you all have taken the time to add your input to the article. I wish all articles existed in this sort of forum.

    Cheers.

  7. I have set up a compatibility table containing up to date CSS3 compatiblity info, and you’d be suprised.

    CSS3 Tests Site: http://geocities.com/seanmhall2003/css3

    CSS3 Compatibility Table: http://geocities.com/seanmhall2003/css3/compat.html

    Safari 1.2 has excellent CSS3 support, and I thank Aaron for acknowledging that fact.

    Those looking for a back button: My rewritten script has one: http://geocities.com/seanmhall2003/SeanSoft/cake.html

    My cake only works perfectly in in (no suprise) Internet Explorer.

    Everyone yells at Microsoft for tying IE7 to the new OS. Apple does the same thing with Safari and everyone…says nothing…??

  8. I really like where you’re going with this, but I have a few improvements I’d like to share with you. I gave each section an id, and the links of the navigation will change the style of that particular section. In addition, I’ve modified the onload of the document in case such a hash has been made–you can check it out here http://www.ilstu.edu/~jcpeter/eatcake.html

    Forgive the ugliness of the page, but it’s really just a technical demo anyhow.

  9. It’s ok. I mean, the only thing that bugged me was

    This does not seperate behaviour and structure. In the JS file place this at the end:
    window.onload = init;
    Also instead of
    return false
    for the support detect, you can just do
    return;
    The latter is probably safer.

  10. The idea of having all information on a single document and then simulating paged content i.e. only showing parts of the document naturally arise an accessibility question from my part. Imo “accessibility” also covers load time (at least for people on slow connections), and if your document spans over endless paragraphs of text and perhaps a few images you user will be very confused when the page finally loads revealing 4 lines of text.

  11. Right on Anders. I just read through all the comments and was about to say something similar.

    I also agree with the sentiments of a page being a page; this example attempts to simulate the paradigm of multiple pages within what is in fact a single page.

    For me, KISS (keep it simple stupid) still reigns supreme.

  12. I agree with Anders and James. I’ve used this technique before, and it works well, though as noted it’s a good idea to put real links in the href and add the javascript to an onClick or something.

    The problem is that this method doesn’t scale well. Not only is page load time significantly impacted, but what happens when your content changes and you have to add sub-categories. Then you have to start setting cookies or something to track sub-states, and things start going south quickly.

    It’s just not a good idea, as it adds a level of complexity that really isn’t necessary. My 2 cents.

  13. This technique does not improve usability. These two articles explains why:

    J. Ryan Baker (2003) – “… participants using the paging condition took significantly longer to read the passages than either the full or scrolling conditions”:
    (2003)http://psychology.wichita.edu/surl/usabilitynews/51/paging_scrolling.htm

    Jakob Nielsen (1997) – “Scrolling Now Allowed”:
    http://www.useit.com/alertbox/9712a.html

  14. This point is not so much related to the general concept of the article but to the tendency for the centered fixed page to jump/reposition itself when it increases in length and needs to be scrolled – I find this an unattractive feature of the site. The ALA pages are always longer than the browser so this does not happen.(IE has scroll bars permanently positioned).

    Is there a simple script to enforce scroll bars in Mozilla even when they are not needed so that when they are needed the page does not jump?

    I love ALA but am a beginner!

  15. I’ve never found a good way to do that in javascript (i thought I had to tie onload to the document object, which never worked for me). I totally agree with you that the more seperation you can get, the better. Anything that minimizes the risk of not being able to use the page is good in my book.

  16. Although I agree with many of you concerning this article’s usefullness and usability in that it may be a stumbling block to some, there are occasions where this approach comes in very handy. I, for one, use almost an identical script to toggle the answers to a list of questions on a FAQ page. It is ideal for a task such as a FAQ page where there is no need to view all at once, and such a page can grow to great lengths. There is also little need for bookmarking of specific sections, or even the need for a back button. However, as a catch-all, at the bottom I do include a tiny section to email the webmaster on any compatibility problems thus ensuring that one always has a way of getting the information they need. *g*

  17. An excellent article on DOM and JavaScript, but not so good for accessibility. As was pointed out earlier, modern tools like JAWS won’t handle a page like this properly. The first rule of achieving true accessibility: keep it simple. (That doesn’t mean keep it DUMB.)

    I do think this code snippet would be excellent, however, for publishing. I used to post multi-page articles all the time, to minimize scrolling, but the process of rewriting one long page when I was done into several smaller pages took a lot of time and led to clumsy mistakes. A script such as this would allow me to post as long a page as I want, and have it automatically be broken up into chunks (I’d still have to add the div’s, but that’s easy.) Fortunately, my articles are never so long that download time would be an issue, although one should bear that in mind as a disadvantage to this approach if you are serving to a broader user base that isn’t necessary broadband-saavy. Two improvements I would make: first, a “Next” link at the bottom of the page to go to the next div without having to go back to the top (too much eye scanning), and maybe “Prev” button to replace the lost “Back” functionality. Second, a link to “Show All” that would display the entire page at once, for those who LIKE to scroll, or want to use full-page text search.

    What I found most interesting in this article, though, was what you didn’t discuss and what other people have been wondering about. I, too, wondered why you didn’t use onClick handlers instead of rewriting the links with fixLinks(), until I tried it, and realized that the Back button failed to redraw the correct div (even with an onLoad handler, it fails in Mozilla). I also wondered why you were rewriting the div’s to use the .hidden class, when you could have just had a class defined for every div, but separate definitions for screen/print media. Then I realized that order of precedence broke that idea, too.

    These would have been excellent additions to the article. Many times, knowing why a more “obvious” solution doesn’t work is just as instructive, if not more so, than the solution that does. But thankfully, that’s why we have the discussion board on ALA — looking past the flamage, most of this was discussed here. Even my suggested improvements. Gotta love it.

  18. I found a seemingly perfect script for what I wanted in this thread http://forums.devshed.com/t77556/s.html

    The opening script tag is malformed, but the rest of the script works perfect in every browser i’ve tried it in and it leaves the content accessable to people who do not have javascript enabled / working properly. I had to remove one line from the script (the //contractcontent(cid) line in the expandcontent section) but otherwise it works out of the box.

    Thanks for the sample reply. I will study it for future reference.

  19. You make it look like the information is devided into multiple pages. A search engine bot will see it as one page and may take some sentences of the content on their search results page. The visitor will get frustrated when the shown part is not found immediatly at the devided page..

  20. There’s a significant issue with taking this approach for people with JavaScript: as the page loads all the content is displayed, and when it finally loads content disappears! Where’s it gone? What’s happened? I didn’t do anything to ask the content to disappear?

    This is a severe usability issue, especially for those using pages over slower connections (as I am now).

    Gabe

  21. i’ve used this with a tabbed list and replaced divs with fieldsets. this is within a CMS interface where theres no need to bookmark or print the sections. being able to break up forms makes a huge difference as these can often be overwhelmingly long.

  22. i’ve implemented the technique in this article on gmail, something of a proof of concept for the article’s example. readers have commented that the page is “much much better and easier to read than most online articles.” so i think this sort of tabbed navigation (especially given the fact that it is also usable without javascript) is very usable.

    the article:
    http://vault.silversand.org/pages/gmail_beta.php

  23. On browsing through this article I got the impression that it would eliminate scrolling. On viewing the finished thing I still had to scroll to read all the text (1280 x 1024 res). So I don’t quite get the point of the script – brilliant as it may be.

    There’s nothing wrong with scrolling, as long as you are either interested in the article or know what you are scrolling too. It’s certainly far better than clicking ‘next’ buttons all the time. I sometimes get the impression that breaking text into chunks has ultimately only one purpose – pushing more adverts at you!

  24. Regarding cocerns for users with slow access, when I use dial-up, I opening several pages in various windows so I can be loading one while reading another. This technique does this automatically.

    Unlimited local calling is relatively rare outside the US so slow speed users are usually concerned about the total time online. I love this technique for it’s potential for entire sites to be viewed offline easily.

    But the designer would have to find a way to let users know that the entire site is loaded and can be viewed offline.

  25. I know I’m coming in three months late here, but if anyone’s still tracking the thread, I wonder that no one has noted (and provided the, perhaps, super clever solution which escapes me still) the quick flash-of-text you get in many browsers when deploying this technique.

    Sure, it’s just all the IEs and definitely a by-product of bandwidth, but it happens even to a relatively meager 20K glossary (for instance). The javascript can’t go to work until the document loads, which means that the body of the text flashes quickly before your eyes before it’s mostly rewritten to “display: none”.

    This flash is esthetically unacceptable. The “obvious” solution (set to “display: none” in CSS and wait for the javascript to display the first element, or whatever) is also unacceptable as anyone with CSS capabilities and disabled javascript won’t see a thing.

    Am I missing something or is there a workaround for that?

    — H

  26. In response to Heidi’s question, as this script relies on an onload event, that is why you see the flash. The only solution I can think of off-hand would be hiding the whole page until the whole thing is loaded and revealing it all in the onload event. To make this work with JS on or off, I would write some JS in the head which wraps an if document.getElementById() around a document.write JS line which writes a css rule stating “body {display: none}” or something similar to hide the page. You could then add a JS function to the onload event to undo this.

    Again, this is purely hypothetical, I have not tried it out for sure, but in theory it should work.

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

I am a creative.

A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
Career