Using JavaScript Unobtrusively

Ed’s Note: This is an extract from the book Professional jQuery by Cesar Otero and Rob Larsen ©2012 John Wiley & Sons. For Source Code, Sample Chapters, the Author Forum and other resources, go to the book’s homepage on wrox.com.

Professional jQuery Book Cover

In the dark days of JavaScript, code like the following was common:

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript">
      function showStuff(){
        var content = document.getElementById("content");
        content.style.display = "block";
      }

      function changeBGColor(elem){
        elem.style.backgroundColor = "green";
      }
    </script>
  </head>
  <body>
    <ul id="content" onLoad="javascript:showStuff();" style="display:none;">
      <li onClick="javascript: changeBGColor(this);">Item 1</li>
      <li onClick="javascript: changeBGColor(this);">Item 2</li>
      <li onClick="javascript: changeBGColor(this);">Item 3</li>
      <li onClick="javascript: changeBGColor(this);">Item 4</li>
    </ul>
  </body>
</html>

You might have cringed at the sight of an embedded CSS style in one of the tags. It’s a common tenet of standards-based web development that presentation information, in the form of CSS styles, should be separated from the content and structure of the page, represented by the HTML. To do this with the preceding example, the styles should be placed in an external CSS file. The improved example should look more like this:

<!DOCTYPE html>
<html>
  <head>
    <style type="text/css" src="myCss.css"></style>
    <script type="text/javascript">
      function showStuff(){
        var content = document.getElementById("content");
        content.style.display = "block";
      }

      function changeBGColor(elem){
        elem.style.backgroundColor = "green";
      }
    </script>
  </head>
  <body>
    <ul id="content" onLoad="javascript:showStuff();" class="contentClass">
      <li onClick="javascript: changeBGColor(this);">Item 1</li>
      <li onClick="javascript: changeBGColor(this);">Item 2</li>
      <li onClick="javascript: changeBGColor(this);">Item 3</li>
      <li onClick="javascript: changeBGColor(this);">Item 4</li>
    </ul>
  </body>
</html>

What’s true for the markup (HTML) and style (CSS) is also true for the behavior (JavaScript). Code representing the behavior of the page should also be removed from the page. Inline event handlers, and inline JavaScript should be avoided wherever possible. This is called unobtrusive JavaScript. The following code sample shows the preceding example rewritten to fully separate style, content and behavior.

<!DOCTYPE html>
<html>
  <head>
    <style type="text/css" src="myCss.css"></style>
    <script>

     (function(document){
          document.onload = function(){
              content.style.display = "block";
          }

          var listItems = document.getElementsByTagName("li");          
          for(i = 0; i < listItems.length; i++){
              listItems[i].onclick = function{
                  listItems[i].style.backgroundColor = "green";    
              }                           
          }          
      })(document);

    </script>
  </head>
  <body>
    <ul id="content" class="contentClass">
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
      <li>Item 4</li>
    </ul>
  </body>
</html>

That’s much better on the eyes — the HTML is semantic and the JavaScript unobtrusive. The markup is exactly that, markup not behavior. But, the JavaScript is fairly verbose. This is where jQuery makes life much easier:

<!DOCTYPE html>
<html>
  <head>
    <style type="text/css" src="myCss.css"></style>
    <script src="jquery.js"></script>
    <script>
      $(document).ready(function(){
          $("#content").show();
          $("li").click(function(this){
              this.css("backgroundColor","green");
          });
      });
    </script>
  </head>
  <body>
    <ul id="content" class="contentClass">
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
      <li>Item 4</li>
    </ul>
  </body>
</html>

Immediately, you’ll notice that the JavaScript code shrank from 11 lines to 7; not bad. The document DOM element is passed into the jQuery function. jQuery wraps it, then applies to the returned wrapper a .ready() method.

The .ready() method registers a handler, which executes code after all of the DOM elements have been loaded, but not necessarily all assets (like images or Flash files) and accepts a function as an argument. For example, the following code sample shows a functionToExecute(), which is passed to .ready() While this pattern works technically, it has the unwanted side effect of adding to the namespace a function that is executed only once.

function functionToExecute(){
   // do stuff here
}
$(document).ready(functionToExecute);

This is a perfect place to use an anonymous function instead.

$(document).ready(function(){
  // do stuff here
});

In the previous example, after the DOM is loaded, the content unordered list is made visible, and then all of the list items in the document are given an onClick event handler, which changes the background color to green using the .css() method.

The .ready() method (or event) used in this example can also take the form

$(function(){
  // on ready code goes here
});

which is functionally equivalent, but more abbreviated. For the remainder of this book, we’ll use the latter form.

Finally, the JavaScript code is moved to a separate file called unobtrusive.js:

<!DOCTYPE html>
<html>
  <head>
    <style type="text/css" src="myCss.css"></style>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript" src="unobtrusive.js"></script>
  </head>
  <body>
    <ul id="content" class="contentClass">
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
      <li>Item 4</li>
    </ul>
  </body>
</html>

With this approach, the artist can do his/her job without affecting the markup of the page, and the programmer can do his/her job without conflicting with the artist’s changes.

This is a good moment to explain chaining, an often used feature of jQuery. Consider the following code:

<!DOCTYPE html>
<html>
  <head>
    <style type="text/css">
      .class1 {
         color: "white",
         background-color: "black",
         width:200px,
         height:100px 
      }
      .class2 {
         color: "yellow",
         background-color: "red",
         width:100px;
         height:200px; 
      }

    </style>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript">
      $(function(){
          $("#myDiv").addClass("class1");
          $("p#blah").removeClass("class1");
          $("p#blah").addClass("class1");
      });
    </script>
  </head>
  <body>
    <div id="myDiv">
      <p id="lorem">Lorem Ipsum</p>
      <p id="blah">blah blah blah</p>
    </div>
  </body>
</html>

Here you have three calls to the jQuery function; each selects an element from the DOM and performs some operation like adding or removing a CSS class to an element. There’s not actually anything wrong with this, but there is a more efficient method. A call to the jQuery function with a selector argument returns an instance of a jQuery object with a collection of selected DOM elements wrapped.

Several jQuery methods also return wrapped objects. For example:

$('body').find('div');

Because .find() returns another wrapper object, you can do another successive method invocation like so:

$('body').find('div').addClass('cssClass');

This is called chaining and it’s not unusual to see long jQuery method chains. Besides readability, this also has a performance advantage over instantiating a new jQuery object, searching through the DOM, and then applying a method. Here’s the rewritten example using chaining instead of separate instances:

<!DOCTYPE html>
<html>
  <head>
    <style type="text/css">
      .class1 {
         color: "white",
         background-color: "black",
         width:200px,
         height:100px 
      }
      .class2 {
         color: "yellow",
         background-color: "red",
         width:100px,
         height:200px 
      }

    </style>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript">
      $(function(){
          $("#myDiv")
           .addClass("class1")
           .find("p#blah")
           .removeClass("class1")
           .addClass("class1");
      });
    </script>
  </head>
  <body>
    <div id="myDiv">
      <p id="lorem">Lorem Ipsum</p>
      <p id="blah">blah blah blah</p>
    </div>
  </body>
</html>

Using Cody Lindley’s terminology, any jQuery method that returns another wrapper object is denoted as a “constructive” method because it can continue a chain, whereas any method that doesn’t return a wrapper (like .text() or .html()) is denoted a “destructive” method.

Writing Less Verbose JavaScript with jQuery

JavaScript, although a very powerful language in its own right, is sometimes verbose. JavaScript frameworks like jQuery offer numerous shortcuts for reducing the amount of code needed to accomplish common web development tasks. You’ve already seen a few:

JavaScript Way

window.onload = function() {
  var elements = document.getElementsByTagName("div");
  for(var = 0; i < elements.length; i++){
      elements[i].style.color = green;
  }
});

jQuery Way

$(function(){
  $("div").each(function(index, value){
      $( this ).css( "color" , "green" )
  });
});

The code feels more concise and abbreviated, and although the difference between the number of lines of code isn’t big overall, you wrote much less. It quickly becomes a habit to use the \$(function(){...}); instead of window.onLoad().

Of course, there are other, deeper benefits to using a library like jQuery. Shorter code is nice, but adding powerful features in a cross-browser way is even better. In this specific example, jQuery grants you the ability to, in a cross-browser way, set multiple callbacks for the onLoad event. Without a cross-browser solution only one callback could be set for events which meant that you could accidentally overwrite methods if you weren’t careful.

As an aside, this problem was originally solved by the community centering on a series of blog posts by the influential JavaScript developer Dean Edwards: http://dean.edwards.name/weblog/2005/09/busted/ and http://dean.edwards.name/weblog/2006/06/again/. One name that appears as an important contributor to the completed solution was none other than the creator of jQuery, John Resig.

You might also like...

Comments

About the author

Dan Maharry

Dan Maharry United Kingdom

Dan Maharry is the editor of DeveloperFusion. He has covered the latest in software development since the mid 90s. Not wishing to preach what he doesn't practice, Dan has also worked as a profes...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Walking on water and developing software from a specification are easy if both are frozen.” - Edward V Berard