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.
- 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.
- 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
- [1] Function to add event handlers to existing ones http://www.scottandrew.com/weblog/articles/cbs-events