AJAX In Action [47]
81
Using multiple event handlers
It’s common practice when scripting DOM nodes using JavaScript to define the script in the window.onload function, which is executed after the page (and therefore the DOM tree) is fully loaded. Let’s say that we have a DOM element on our page that will display dynamically generated data fetched from the server at regular intervals once the page is loaded. The JavaScript that coordinates the data fetching and the display needs a reference to the DOM node, so it gets it by defining a window.onload event: window.onload=function(){
displayDiv=document.getElementById('display');
}
All well and good. Let’s say that we now want to add a second visual display that provides alerts from a news feed, for example (see chapter 13 if you’re interested in implementing this functionality). The code that controls the news feed display also needs to grab references to some DOM elements on startup. So it defines a window.onload event handler, too:
window.onload=function(){
feedDiv=document.getElementById('feeds');
}
We test both sets of code on separate pages and find them both to work fine. When we put them together, the second window.onload function overwrites the first, and the data feed fails to display and starts to generate JavaScript errors. The problem lies in the fact that the window object allows only a single onload function to be attached to it.
Limitations of a composite event handler
Our second event handler overrides the first one. We can get around this by writing a single composite function: window.onload=function(){
displayDiv=document.getElementById('display');
feedDiv=document.getElementById('feeds');
}
This works for our current example, but it tangles together code from the data display and the news feed viewer, which are otherwise unrelated to each other. If we were dealing with 10 or 20 systems rather than 2, and each needed to get references to several DOM elements, then a composite event handler like this would become hard to maintain. Swapping individual components in and out would become difficult and error prone, leading to exactly the sort of situation that we Licensed to jonathan zheng 82 CHAPTER 3 Introducing order to Ajax described in the introduction, where nobody wants to touch the code in case it should break. Let’s try to refactor a little further, by defining a loader function for each subsystem: window.onload=function(){ getDisplayElements(); getFeedElements(); } function getDisplayElements(){ displayDiv=document.getElementById('display'); } function getFeedElements(){ feedDiv=document.getElementById('feeds'); } This introduces some clarity, reducing our composite window.onload() to a single line for each subsystem, but the composite function is still a weak point in the design and is likely to cause us trouble. In the following section, we’ll examine a slightly more complex but more scalable solution to the problem. The Observer pattern It can be helpful sometimes to ask where the responsibility for an action lies. The composite function approach places responsibility for getting the references to DOM elements on the window object, which then has to know which subsystems are present in the current page. Ideally, each subsystem should be responsible for acquiring its own references. That way, if it is present on a page, it will get them, and if it isn’t present, it won’t. To set the division of responsibility straight, we can allow systems to register for notification of the onload event happening by passing a function to call when the window.onload event is fired. Here’s a simple implementation: window.onloadListeners=new Array(); window.addOnLoadListener(listener){ window.onloadListeners[window.onloadListeners.length]=listener; } When the window is fully loaded, then the window object need only iterate through its array of listeners and call each one in turn: window.onload=function(){ for(var i=0;i func.call(); } } Licensed