AJAX In Action [73]
We can fix things up by passing a reference to the Button object to the DOM
element, that is, by modifying our constructor like this:
function Button(value,domEl){
this.domEl=domEl;
this.value=value;
this.domEl.buttonObj=this;
this.domEl.onclick=this.clickHandler;
}
The DOM element still doesn’t have a value property, but it has a reference to the Button object, which it can use to get the value. We finish up by altering the event handler like this:
Licensed to jonathan zheng The Controller in an Ajax application 137 Button.prototype.clickHandler=function(){ var buttonObj=this.buttonObj; var value=(buttonObj && buttonObj.value) ? buttonObj.value : "unknown value"; alert(value); } The DOM node refers to the Button, which refers to its value property, and our event handler does what we want it to. We could have attached the value directly to the DOM node, but attaching a reference to the entire backing object allows this pattern to work easily with arbitrarily complex objects. In passing, it’s worth noting that we’ve implemented a mini-MVC pattern here, with the DOM element View fronting a backing object Model. That’s the classic event model, then. The main shortcoming of this event model is that it allows only one event-handler function per element. In the Observer pattern that we presented in chapter 3, we noted that an observable element could have any number of observers attached to it at a given time. When writing a simple script for a web page, this is unlikely to be a serious shortcoming, but as we move toward the more complex Ajax clients, we start to feel the constraint more. We will take a closer look at this in section 4.3.3, but first, let’s look at the more recent event model. 4.3.2 The W3C event model The more flexible event model proposed by the W3C is complex. An arbitrary number of listeners can be attached to a DOM element. Further, if an action takes place in a region of the document in which several elements overlap, the event handlers of each are given an opportunity to fire and to veto further calls in the event stack, known as “swallowing” the event. The specification proposes that the event stack be traversed twice in total, first propagating from outermost to innermost (from the document element down) and then bubbling up again from the inside to the outside. In practice, different browsers implement different subsets of this behavior. In Mozilla-based browsers and Safari, event callbacks are attached using addEventListener() and removed by a corresponding removeEventListener(). Internet Explorer offers similar functions: attachEvent() and detachEvent(). Mike Foster’s xEvent object (part of the x library—see the Resources section at the end of this chapter) makes a brave attempt at creating a Façade (see chapter 3) across these implementations in order to provide a rich cross-browser event model. There is a further cross-browser annoyance here, as the callback handler functions defined by the user are called slightly differently. Under Mozilla browsers, Licensed to jonathan zheng 138 CHAPTER 4 The page as an application the function is invoked with the DOM element receiving the event as a context object, as for the classic event model. Under Internet Explorer, the function context is always the Window object, making it impossible to work out which DOM element is currently calling the event handler! Even with a layer such as xEvent in place, developers need to account for these variations when writing their callback handlers. The final issue to mention here is that neither implementation provides a satisfactory way of returning a list of all currently attached listeners. At this point, I advise you not to use the