Summary

I'm surprised that the noscript element is conforming in HTML5, as there are much better techniques for ensuring that pages work with or without JavaScript. Despite early accessibility advice advocating use of the noscript element, best practice is to use unobtrusive JavaScript for progressive enhancement, rather than relying on fallback content.

Author: Gez Lemon

The noscript Element

The noscript element was a well-intentioned idea to provide fallback content when scripting isn't available. In reality, it hasn't been used very well. The fallback for the majority of noscript content is merely to inform the user that their browser does not support JavaScript, which isn't very helpful.

<script type="text/javascript">
  // Do something cool
</script>
<noscript>
 <p>Your browser does not support JavaScript!</p>
</noscript>

In fairness, some people did use it correctly, and offered an alternative option through the noscript element.

<script type="text/javascript">
  // Do something cool
</script>
<noscript>
 <p>
  <a href="http://somewhere.com/latestdata.html">
   Download the latest statistics
  </a>.
 </p>
</noscript>

Accessibility Advice

WCAG 1.0 included guidance on providing a fallback mechanism for scripts. The relevant checkpoints were checkpoint 1.1, checkpoint 6.2 and checkpoint 6.3:

WCAG 1.0 Checkpoint 1.1

Provide a text equivalent for every non-text element (e.g., via "alt", "longdesc", or in element content). This includes: images, graphical representations of text (including symbols), image map regions, animations (e.g., animated GIFs), applets and programmatic objects, ASCII art, frames, scripts, images used as list bullets, spacers, graphical buttons, sounds (played with or without user interaction), stand-alone audio files, audio tracks of video, and video. [Priority 1]

WCAG 1.0 Checkpoint 6.2

Ensure that equivalents for dynamic content are updated when the dynamic content changes. [Priority 1]

WCAG 1.0 Checkpoint 6.3

Ensure that pages are usable when scripts, applets, or other programmatic objects are turned off or not supported. If this is not possible, provide equivalent information on an alternative accessible page. [Priority 1]

The techniques document for WCAG 1.0 suggested using the noscript element as an alternative presentation of scripts with the following example:

<SCRIPT type="text/tcl">
 // script to show a billboard of sports scores  
</SCRIPT>
<NOSCRIPT>     
  <P>Results from yesterday's games:</P> 
  <DL>
    <DT>Bulls 91,  Sonics 80.
    <DD>
    <A href="bullsonic.html">Bulls vs. Sonics 
        game highlights</A> 
    // more scores ...  
  </DL>
</NOSCRIPT>

Fortunately, WCAG 2.0 doesn't mention the noscript element in the techniques document.

Unobtrusive JavaScript

Instead of relying on fallback mechanisms, current best practice is to use progressive enhancement. This basically means ensuring that the page works without JavaScript, and then adding the JavaScript functionality progressively. This technique has been used for many years now, supported by many standards-advocates including Christian Heilmann who has great resources on Unobtrusive JavaScript techniques.

Using unobtrusive JavaScript techniques encourages authors to think in terms of separate layers, as the script itself is never written in the document. The basic layers are content (in markup), presentation (with CSS), and behaviour (the scripting part). The result is more robust than relying on JavaScript, and then depending on a fallback mechanism such as using noscript.

Style Sheet Switcher Example

Consider an example of a style sheet switcher. This is easy if you separate the presentation and behaviour using CSS and JavaScript. First, a quick reminder about style sheets.

Persistent Style Sheets

A persistent style sheet is always enabled. If the rules for a document are contained in a single style sheet, then this is the way you will link to the style sheet. If the document has more than one style sheet associated with it, the basic rules may be placed in this style sheet. The relationship is specified with a rel attribute value of stylesheet, and no title attribute is provided.

<link href="persist.css" rel="stylesheet" type="text/css">

Preferred Style Sheets

A preferred style sheet is enabled by default, but may be switched by the user for an alternate style sheet. A preferred relationship is specified with a rel attribute value of stylesheet, and a title attribute is also provided. You may specify more than one preferred style sheet by specifying the same title for each. The preferred style sheets will be grouped, and enabled and disabled together. If more than one group of preferred style sheets are specified, the first group will take precedence over the other groups.

Alternate Style Sheets

An alternate style sheet is one that may be selected by the visitor as an alternative to the preferred style sheet. An alternate relationship is specified with a rel attribute value of alternate stylesheet, and a title attribute is also provided. Like preferred style sheets, alternate style sheets may be grouped by giving them the same title.

The following example uses a persistent, preferred, and alternate style sheet, allowing the visitor to customise the look of the site to their taste.

<link href="persist.css" 
      rel="stylesheet" type="text/css">
<link href="prefer.css" 
      rel="stylesheet" title="Regular" type="text/css">
<link href="alt.css"
     rel="alternate stylesheet" title="High Contrast" type="text/css">

To create a style sheet switcher, you would start by providing a link to a server-side page that chose the style sheet the user chose.

<a href="switchstyle.php?s=2">High Contrast</a>

This would essentially work by setting a cookie to remember the user's choice, and then examining the cookie when writing out the style sheets to ensure they're the user's choice. For example, if the user's choice was high contrast, you would write the style sheets out as follows:

<link href="persist.css" 
      rel="stylesheet" type="text/css">
<link href="prefer.css" 
      rel="alternate stylesheet" title="Regular" type="text/css">
<link href="alt.css"
     rel="stylesheet" title="High Contrast" type="text/css">

That is the basic functionality, and works well using the regular link to get the user's choice and arrange the style sheets to their preference.

Enhancing with JavaScript

The functionality could be enhanced by using JavaScript to switch the styles, as this would save the page being re-fetched from the server; updating the user's choice immediately. By taking an unobtrusive JavaScript approach, we ensure the page works without scripting, and then add the functionality using script. We would need a hook for the script, which could be an id or a class. For this example, I'll use a class name of switch.

<li><a href="switchstyle.php?s=1" class="switch">Regular</a></li>
<li><a href="switchstyle.php?s=2" class="switch">High Contrast</a></li>
<li><a href="switchstyle.php?s=3" class="switch">Single Column</a></li>

We could then use something like the following in a separate file to add the scripting for any anchor element with a class name of switch.


// Function to return a collection of elements by class name
function getElementsByClassName(objElement, strTagName, strClassName)
{
   var objCollection = objElement.getElementsByTagName(strTagName);
   var arReturn = [];
   var strClass, arClass, iClass;

   for(var iCounter=0; iCounter<objCollection.length; iCounter++)
   {
      strClass = objCollection[iCounter].className;
      if (strClass)
      {
         arClass = strClass.split(' ');
         for (iClass=0; iClass<arClass.length; iClass++)
         {
            if (arClass[iClass] == strClassName)
            {
               arReturn.push(objCollection[iCounter]);
               break;
            }
         }
      }
   }

   objCollection = null;
   return (arReturn);
}

// Set the cookie so that it can be checked when new pages are
// returned
function setCookie(strName, strValue)
{
   var objDate = new Date();
   
   // Set expiration as 1 year in seconds
   objDate.setTime(objDate.getTime() + 31536000);

   document.cookie = strName + '=' + strValue + '; expires=' + objDate.toGMTString() + '; path=/';
}

function switchStyle(objAnchor)
{
   var strLinkPhrase = objAnchor.firstChild.data;
   var iCounter;

   setCookie("styleopt", strLinkPhrase);

   for(iCounter=0; iCounter<document.styleSheets.length; iCounter++)
   {
      if (document.styleSheets[iCounter].title != strLinkPhrase)
      {
         document.styleSheets[iCounter].disabled = true;
      }
      else
      {
         document.styleSheets[iCounter].disabled = false;
      }
   }

   return false;
}

function  addStyleEvents()
{
   var objSwitch = getElementsByClassName(document, 'a', 'switch');
   var iCounter;

   for (iCounter=0; iCounter<objSwitch.length; iCounter++)
   {
      objSwitch[iCounter].onclick = function(){return switchStyle(this);};
   }

   return true;
}

function init()
{
   addStyleEvents();
}

window.onload = init;

Conclusion

As unobtrusive JavaScript techniques encourage developers to separate behaviour from content (as CSS encourages developers to separate presentation from content), the result is more robust and much easier to maintain, as the JavaScript is included in a separate page. Retaining the noscript element in HTML5 is good for backwards compatibility, but personally, I would have preferred it to be deprecated and developers discouraged from using it and use unobtrusive scripting techniques instead.

Category: Accessibility.

Comments

  1. [say-no-to-noscript.php#comment1]

    Hi Gez, thanks for the nice article. I'd like to add that I've heard stories of high security network environments like banks, where JavaScript is enabled in the client, but proxies filter any JavaScript. Those clients would be invisible to statistics, also the noscript tag won't work. That's another reason why noscript is useless and why setting a "js" class on the body element or other enhancements by JavaScript are superior for toggling content.

    Posted by Martin Kliehm on

Comments are closed for this entry.