Unobtrusive Javascript: Introduction

Unobtrusive Javascript is a way of using Javascript that cleanly separates your Javascript from your HTML code.  Instead of coding Javascript actions within your document (e.g., using an onchange=”…” handler), your Javascript actions are contained within an external script that inserts actions into your document based on CSS class names or element ids that you have pre-determined.

For example, you might have a link that toggles the presence of another element:

<script src="/js/prototype.js" type="text/javascript"></script>
. . .
<a href="#" onclick="$('toggle').toggle(); return false;">Toggle</a>
<div id="toggle" style="display:none">Some content here</div>

There are some problems with this, however.  First, the code is cluttered with messy Javascript.  Second, our code doesn’t fail gracefully if Javascript is not enabled — the “display:none” style will cause our div not to be displayed, but it would be better if the default were for it to be displayed when Javascript is not active.  Of course, we could create a body onload action to workaround this problem, but that clutters our HTML with even more Javascript and also causes the toggle behavior to be spread across several elements.

Unobtrusive Javascript solves these problems by defining and collecting the dynamic behavior in a single separate Javascript file.  The HTML document now contains only HTML, plus a few additional CSS class selectors that our Unobtrusive Javascript uses to locate elements that it should take action on.  Using the above example, we might create an Unobtrusive Javascript solution that looks like this:


Event.onReady(function() {
    $$('.auto_toggle').each(function(elem) {
        var element = elem;
        var anchor = $( + "_anchor");
        anchor.observe('click', function(ev) { element.toggle(); ev.stop(); });


<script src="/js/prototype.js" type="text/javascript"></script>
<script src="/js/lowpro.js" type="text/javascript"></script>
<script src="/js/autotoggle.js" type="text/javascript"></script>
. . .
<a href="#" id="toggle_anchor">Toggle</a>
<div id="toggle" class="auto_toggle">Some content here</div>

Before we defined an onclick action on our anchor, but now we set a special CSS class on the div.  By convention, we give the anchor an id based on the id for the div.  Our external Javascript code then finds all divs with the class “auto_toggle”, and inserts an onclick action into the anchor whose id matches the div’s id, with the suffix “_anchor”.  If Javascript is not active, the div will be displayed.

Going into more detail, here is what our unobtrusive toggle handler does:

  1. For DOM help, it uses the Prototype and Low Pro libraries (although it is possible to write unobtrusive Javascript using other libraries like jQuery).
  2. The call to Low Pro’s Event.onReady() registers a function to be called when the page’s DOM is loaded.  We’ve provided an anonymous function to this call that performs our unobtrusive action.
  3. Our function calls Prototype’s $$() function to locate the list of all elements matching the CSS selector specification “.auto_toggle”; i.e., any element having a class named “auto_toggle”.  It then iterates over this list using the Prototype Array.each() method, providing an anonymous element handler function to be called for each element.
  4. Our element handler creates variables referencing the matching element, and also locating the anchor element that has the id matching the current element’s id with the added suffix “_anchor”.  It then calls the Prototype Element.hide() method to ensure that the toggle element starts out hidden.
  5. Our element handler then calls the Prototype Element.observe() method to register a handler for whenever the toggle anchor is clicked.  For our click handler we provide an anonymous function that calls the Prototype Element.toggle() method to toggle the visibility of the target element and then stop the event by calling Event.stop() (stopping the event is the same as returning false in an onclick event; this causes the anchor target URL not to be visited).  You may notice that our click handler function is a Javascript closure — it references variables in the calling function, and these variables will be available to it even when the calling function has ended.

This is a simple example, but we could take it further.  For example, we could define the anchor as a span element, and our unobtrusive code could replace it with an anchor element only if Javascript is enabled.  That way, users without Javascript won’t see anything clickable on the toggle action.  Or, if we knew that every toggle-able div had the text “Toggle” for its anchor, we could remove the toggle anchor altogether and have our unobtrusive code insert it.

Apart from keeping our HTML code clean and simple, one of the great advantages of Unobtrusive Javascript is that we can build up a library of powerful unobtrusive actions that we can reuse with minimal changes to our HTML.  Growing more complex than the example we’ve considered here, we could insert dynamic pop-up calendars for every form input element that has a “date_calendar” CSS class.  Or we could create code to find every textarea element in the document and cause these textareas to be dynamically resized as users type text into them.  We’ll start to build up our Unobtrusive Javascript library by considering examples like these in subsequent posts.

4 thoughts on “Unobtrusive Javascript: Introduction

  1. One thing to remember with this technique is that it works best for small documents. Most of the Javascript techniques to attach event handlers to DOM nodes won’t work until the DOM has been fully loaded, but some browsers will begin to display page content before that point.

    If the user clicks on a link that has not had the Javascript handler attached to it yet, they’ll get the default link behavior, whatever that is.

    The bigger the document, the greater the chance that the user will be able to interact with the page before the dynamic behavior is in place.

  2. Thanks, Jordan, that’s a good point. That’s an argument in favor of defining the anchor as a <span> instead and then having the handler replace it with an anchor.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s