A List Apart

Menu
Issue № 177

Let Them Eat Cake

by Published in Browsers, JavaScript, Accessibility, Usability · 85 Comments

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.

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

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

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 = "[removed]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=”[removed]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

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 = "[removed]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 = "[removed]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

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

Load Comments