Understanding the Browser Event Model

Before getting into how jQuery handles events, and how it saves you a lot of grief, let’s review JavaScript event models. JavaScript was originally introduced as LiveScript back in 1995. Getting in the DeLorean and jumping back in time to 1996, Netscape Navigator and Microsoft Internet Explorer 3 were all the rage among the young crowd. Table 5-1 illustrates the various event models in play during those primordial days of web scripting.

DOM level 0 refers to the document model support given by Netscape Navigator 2.0, which is also adopted by later browsers. This level is not a formal standard, but all major browsers are level 0 compatible. The DOM provides an interface for accessing and modifying the content, style, and structure of an HTML document. DOM level 0 events are handled either inline, with an “on” attribute added to the element with JavaScript code as the attribute value, or by selecting the element inside JavaScript code and then binding an anonymous function as the event handler. The following snippet demonstrates the former method:

<html>

<head>

<style type=”text/css”>

.clickDiv {

width:100px;

height:100px;

background-color:blue;

}

</style>

<script type=”text/javascript”>

function hitMe(){

var txtNode = document.createTextNode(” clicked “);

var br = document.createElement(“br”);

document.getElementsByTagName(“body”)[0].appendChild(txtNode);

document.getElementsByTagName(“body”)[0].appendChild(br);

}

</script>

</head>

<body>

<div class = “clickDiv” onclick=”hitMe();”>

</div>

</body>

</html>

Code snippet is from onclick.txt

The JavaScript in this example is obviously intrusive. I’ve bound to the onclick event the hitMe() method. The previous example can also be written as:

<html>

<head>

<style type=”text/css”>

.clickDiv {

width:100px;

height:100px;

background-color:blue;

}

</style>

<script type=”text/javascript”>

document.getElementById(“clickDiv”).onclick = function(event){

document.getElementsByTagName(“body”)[0].appendText(“clicked”);

}

</script>

</head>

<body>

<div id=”clickDiv” class=”clickBox”>Click Here</div>

</body>

</html>

Code snippet is from better-onclick.txt

which now separates the control logic from the structure. This still suffers from a few serious setbacks. The function bound to the onclick event accepts a vent object. While most browsers accept the standard event object, Internet Explorer uses window.event, or in other words, binds the event object to the global window object. Using feature detection, preferred over browser detection, your event handler script should do something like the following:

var evt = (window.event ? window.event : event);

Furthermore, the standard event object is not the same as IE’s Event object, and supports a slightly different set of properties. For cross-browser scripts, this is very annoying and breaks compatibility. Table 5-2 illustrates some of the similarities/differences between the IE Event object and the standard Event object.

 

At times, you may want to bind several different handlers to the onclick event of the same element. In DOM level 0, binding will overwrite any other method already bound to the event.

The solution is found in DOM level 2 events. You probably noticed a lack of DOM level 1 events; it’s because they don’t exist. Level 2, on the other hand, is a more advanced model than level 0, and is supported by most browsers except for IE version 8 and earlier; more on this deviation later. In this model, instead of assigning an event handler function as an object property each page element in the DOM has a method called .addEventListener(), which allows you to add more than one event handler to an element. For example:

<html>

<head>

<script src=”http://code.jquery.com/jquery-1.7.1.js”></script>

<script>

$(function(){

var eventType = “click”;

var useCapturePhase = false;

var handlerl = function(event){ /* handler code here */ };

var handler2 = function(event){ /* handler code here */ };

$(“#element”).addEventListener(eventType, handlerl, useCapturePhase);

$(“#element”).addEventListener(eventType, handler2, useCapturePhase);

});

</script>

</head>

<body>

</body>

</html>

Code snippet is from addEventListener.txt

The addEventListener() accepts three arguments: the event type (in this case it’s “click”), the function that will handle the event, and a Boolean value for using the capturing phase. “What’s that?” you ask.

1. Event Capturing and Bubbling

Consider the situation of nested elements with the same kind of event type, for example an onclick event type. Which element should have its onclick event handler executed first, the inner element or the outer element? Originally, Netscape decided that the handling should be from outer to inner, called capturing, as Figure 5-1 illustrates.

Microsoft went with the opposite approach where the inner element’s “onclick” handler should be executed before the outer element’s, or the bubbling phase, as shown in Figure 5-2.

The third argument to the .addEventListener() method is a Boolean flag to determine whether or not you should use the capture phase.

But, you may not want event propagation or bubbling to occur at all; this can also be controlled in JavaScript. In browsers that follow the W3C standard, a call to the method .stopPropagation() is used. In older versions of IE, the property cancelBubble of the event object is assigned a value of true.

The following example demonstrates event capturing:

<html>

<head>

<style type=”text/css”>

.diviClass {

width: 300;

height: 300;

background-color: blue;

}

.div2Class {

width: 200;

height: 200;

background-color: green;

}

.div3Class {

width: 100;

height: i00;

background-color: red;

}

</style>

<script src=”http://code.jquery.com/jquery-1.7.1.js”></script>

<script>

$(function(){

function f(id){

document.body.innerHTML += “<p>executing handler for div ” + id + “</p>”;

}

document.getElementById(“div1”).onclick = function(){

f(“1”);

}

document.getElementById(“div2”).onclick = function(){

f(“2”);

}

document.getElementById(“div3”).onclick = function(){

f(“3”);

}

});

</script>

</head>

<body>

<div id=”div1” class=”div1Class”>

<div id=”div2” class=”div2Class”>

<div id=”div3” class=”div3Class”>

</div>

</div>

</div>

</body>

</html>

Code snippet is from stop-propagation.txt

The previous example is tested with Google Chrome; you can see the results in Figure 5-3. After clicking on div 3 each successive handler is called.

IE8 and below do not support DOM level 2 like most other browsers. For example, the IE event model uses attachEvent instead of addEventListener. As mentioned earlier, IE’s Event object doesn’t completely support all the same methods and properties of the other W3C-compliant browsers; even the event names differ. IE offers an event propagation similar to bubbling, but not exactly the same.

Source: Otero Cesar, Rob Larsen (2012), Professional jQuery, John Wiley & Sons, Inc

Leave a Reply

Your email address will not be published. Required fields are marked *