Article

Home » Client-side Coding » JavaScript & Ajax Tutorials » Deck the Halls with Unobtrusive JavaScript
SitePoint Feature Article

About the Author

Ara Pehlivanian

Ara Pehlivanian Ara has been working on the Web since 1997. He's been a freelancer, a webmaster, and most recently, a front-end architect and practice lead for Nurun, a global interactive communications agency. Ara's experience comes from having worked on every aspect of web development throughout his career, but he's now following his passion for web standards-based front-end development. When he isn't teaching about best practices or writing code professionally, he's maintaining his personal site at http://arapehlivanian.com/.

View all articles by Ara Pehlivanian...

Deck the Halls with Unobtrusive JavaScript

By Ara Pehlivanian

December 19th, 2007

Reader Rating: 9

Page: 1 2 3 Next

'Tis the season to be jolly, and 'tis also an exciting time to be a JavaScript developer. With the advent of the Web 2.0 craze, a new breed of JavaScript developer was born. Modern JavaScript programmers take their trade very seriously and count themselves among the fraternity of "real" programmers. A key component in a JavaScript programmer's arsenal is the methodology of unobtrusive JavaScript -- the idea that a web page's behavior should remain separate from its structure. The idea for unobtrusive JavaScript grew out of the Web Standards movement, which advocated that web pages should be separated into three layers -- structure (HTML), presentation (CSS), and behavior (JavaScript) -- and that each additional layer should enhance the previous one.

Traditionally, most, if not all, event-based JavaScript was written directly into the web page's markup in the form of event handler attributes such as onclick, onfocus, onload, onmouseover, and onmouseout. Also, all dynamically generated markup took the form of in-place document.write statements. But none of this sits well with the principle of unobtrusive JavaScript.

Just as presents aren't what Christmas is all about, neither is JavaScript what a web page is all about. A page should be functional without any scripting, rather being dependent on it. JavaScript function calls and instructions that are inextricably intertwined with markup create just such a dependency. They also reduce the portability of the document's HTML and make it progressively harder to maintain as the site's page count increases. But worst of all, they'll get you onto Santa's Naughty list -- and no one wants to be there!

Buying the Presents

Unobtrusive JavaScript dictates that scripting should ideally reside in a separate document, and hook into a web page through HTML id and class attributes. Likewise, all dynamically generated markup should be inserted into the DOM after it has been built using made-for-the-purpose DOM methods. This way, if a page is already functional before JavaScript is added, the behavioral layer becomes an enhancement to the document rather than a dependency -- kind of like icing on a cake, or gifts on Christmas.

Now we don't live in an ideal world. Sometimes we find ourselves working with multi-generational projects that haven't been well documented or maintained. Other times, our mandate (and therefore budget) doesn't cover a complete revamp or optimization of the existing code that we're being asked to modify. Web pages aren't always light, networks aren't always fast, and, in a team development setting, developers don't always have full control over all of a page's components. Keeping that in mind, let's take a look at a nasty side effect of the implementation of unobtrusive JavaScript when conditions aren't optimal.

Bringing the Presents Home Through the Front Door

There are two ways to load JavaScript into an HTML document. The traditional approach is to place a <script> tag in the document's head and trigger your functions using the window object's onload event. We'll call this "front loading" because the scripts are loaded before the page's contents in the document's <head>, before the DOM is built. Front loading isn't a good idea because it's vulnerable to timing issues. For example, a browser downloads, parses, and executes JavaScript wherever it's encountered in the web page's source, so any JavaScript in the document's <head> will delay the page's rendering until that process is complete. More importantly, once that's done and the page is rendered, functions tied to the window object's onload event may not be triggered right away. That's because the event is only triggered once the browser has finished downloading all of the page's dependencies -- including the several hundred kilobytes' worth of images and other media often found on web pages today.

Front loading can cause an undesired effect in which the visitor sees a full, JavaScript-free page for a period during which he or she is able to click on anything. So, for example, if an anchor was meant to trigger a modal popup (a div on CSS steroids masquerading as a popup) it wouldn't do so during this loading period, because the JavaScript required to set up the modal behaviour would not yet have executed, since the window object's onload event wouldn't have fired. Instead, once the anchor was clicked, the browser would just send the user to the URI found in the anchor's href attribute. The end result would be that the page wouldn't function as intended. Sure, having a valid URI in the anchor still lets the visitor continue to use the site, but it isn't the desired or intended effect.

Here's what a front-loaded, unobtrusive script looks like:

front-load.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
 <head>
   <title>Welcome</title>
   <script>
     function initSignin() {
       var signin = document.getElementById("signin");
       signin.onclick = function () {
         /*
          * Sign-in code that creates a modal
          *  popup goes here.
          */
         alert('Pretend this is a modal popup...');
         return false; // Stop the anchor's default behavior
       };
     }
     window.onload = initSignin;
   </script>
   <link rel="stylesheet" type="text/css" media="all" href="style.css">
 </head>
 <body>
   <p class="xmas">
     <a href="/signin/" id="signin">Sign in</a>
   </p>
   <!-- 700 kilobytes worth of media goes here -->
 </body>
</html>

You'll note that the execution of our initSignin function is deferred till after the page's contents are loaded. Within the initSignin function we stop the "Sign in" anchor's default behavior by returning the value false to the anchor. However, the browser won't trigger the window object's onload event until it's downloaded seven hundred kilobytes of media. So, until it's done getting those files, initSignin won't run, and our link's behavior won't be overridden.

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links

Follow SitePoint on...