Call of the wild (scripts)

The main technique we use to keep our Javascript unobtrusive is to keep it in a single file rather that messing up the markup of the page. To execute the functions in our .js file, we need to call them when the page has loaded. This can be achieved in various ways, each with its benefits and problems

The oldschool way

Back when we were young and innocent, we added the onload to the body element.

HTML:
<body onload="foo();">

Which is something that we should remember that we have done wrong and not do any more!

If we call the script(s) in the body element we have achieved nothing, as we still mix markup and event calls. What we need to do is to separate the call out into the .js file.

The separated way

We do this by attaching the call to the onload event of the window object.

When we only use one function, we don't use the parenthesis at the end of the function name, as that would send back the result of the function rather than trigger the function.

If we have more than one function, we have to use an anonymous function that calls the others, this time with parenthesis.

Javascript:
window.onload=foo;

or

window.onload=function(){
 foo();
 bar();
 baz();
}

This method of calling functions is of course not only applicable to the window object. As shown before, we can add it to any object in the page. It is supported by any Javascript/DOM-able browser out there.

The drawback of this solution is that we are not totally unobtrusive. If we had more than one .js include, they'd overwrite each other's onload calls.

The really unobtrusive way

There is an option to add event handlers to already existing ones. Sadly enough, different browsers handle this functionality differently, and IE on Macs not at all.

Scott Andrew wrote a nice reusable function[1] that does exactly that, and you invoke it by giving it the object to attach the event to, the event type, and the function name.

function addEvent(obj, evType, fn){ 
 if (obj.addEventListener){ 
   obj.addEventListener(evType, fn, false); 
   return true; 
 } else if (obj.attachEvent){ 
   var r = obj.attachEvent("on"+evType, fn); 
   return r; 
 } else { 
   return false; 
 } 
}
addEvent(window, 'load', foo);
addEvent(window, 'load', bar);

The obvious drawback being that it won't attach anything on IE on macintosh systems.

The voice of Opera

I got an email from Opera Technical Support explaining that the previous example was flawed, that is why it actually is different to the one on Scott's site:

The addEvent function is not entirely standards-compliant. It will work for now in FireFox but once they fix this bug it will not work reliably in FireFox anymore. It probably does not do what you want in Opera, it supports the standards correctly. The issue is that you set up a "capturing" event handler by using "true" as the third argument to the addEventListener function:
if (obj.addEventListener){ 
   obj.addEventListener(evType, fn, true);
That true should be false. If it is true, the event listener should not fire for any element the event listener is registered on. For instance, if you use this with a LI element it will not do anything unless the LI has child elements. Test in Opera :)

More reading

Events are a very complex and confusing topic in Javascript. For web site development, the examples here are sufficient, but if we go into web application development, we might want to go further. Check the links in the useful articles appendix for more information about events and how to apply them to elements.

Why don't you try it?

Simply download the demo HTML and try one of the following tasks. Follow the solution links to see one possible solution. The solutions have the Javascript inline as a demonstration, not in an extra document where they should be. This was done to help you see the necessary markup in one document rather than two.

  1. Take this table and use an unobtrusive function to make every second row another colour. Add another colour when the user hovers over the row. Solution table.
  2. Add a function to each P element with the class trigger to collapse and expand the following element (by setting its display style to block or none). Solution collapse.

Links

Creative Commons License Unless otherwise expressly stated, all original material of whatever nature created by Christian Heilmann and included in this web site and any related pages is licensed under the Creative Commons License.