Looking for the previous guiStuff?
It's still here, the content didn't go anywhere. You may want to check out this new guiStuff though -- It's rather informative.
References/Tutorials:
Intro Documents:
guiStuff:
::Stuff for the multi-spec coder;
Coding, formats, standards, and other practical things.
Home // JavaScript // Events: Part 1
<!-- JavaScript
Events: Part 1
Events is the umrella term used to describe anything and everything that actively takes place within a document. JavaScript is an event-driven programming language, which means that it is 'reactive', for the most part. The vast majority of the functions and scripts are triggered by events that take place in the document. These events, for the most part, are triggered by user action.Strictly speaking, a programmer doesn't have to design using a 'reactive paradigm': One could, for example, set a single instance of a cycle, like a heartbeat, and have everything in the program react to that "primary timer". This is, in fact, how Digital Systems operate -- there's a primary clock-cycle that all other systems derive their actions from. Of course, JavaScript uses the event-driven model because it's more practical in most situations for which is was designed for, when operating within a document. Remember that JavaScript, or rather the standard of ECMAScript-262, is a broader-purpose scripting language, and if you were to use it, for example, for server-side operations, this type of event-driven model would be within a completely different context.
But we're here to talk about JavaScript in the browser, or, more broadly, within an interpreter that's using the DOM (that is to say, we have JavaScript opertating on a document in real-time). The browser generates an event whenever something occurs on the page. This may be triggered by many things: The document may have finished loading, the mouse pointer may have moved over an element's area, a keyboard button may have been clicked, a mouse button may have been clicked, etc..
Generally speaking, there are two 'levels' of events that are being used today: The original, 'basic' event model, also known as the DOM Level 0 API, which is supported across most browsers in the same manner, mostly because it's been around for quite some time. It provides enough functionality for Web Sites and simple Web Applications, but you'd be hard-pressed to develop a proper Web Application and/or a Web-based Service using only the basic event model. For this purpose, there's the Standard Event Model, which is defined by the DOM Level 2.
Normally, I'd just say this is general background material and get right to the code, but it's not that simple this time. Internet Explorer does not support the DOM Level 2 Event Model. It defines a separate event model, usually referred to as the Internet Explorer Event Model. This means that, like so many times when developing for the web, we're forced to 'fork' our code for two platforms: Internet Explorer, and, well, everyone else. The only bright spot here is that while the two models are very different in some fundumental ways, it's not that difficult to create cross-browser functions that will handle both cases for us without having to literally double our code. Also, when there's no need for 'high-fidelity event handling', you can always use the basic event model, which is supported in the same manner by most browsers.
The Basic Event Model
When using the basic event model, you're basically assigning event handlers as element attributes. You can do this by placing the script as the attribute's value, like so:
<span onclick="alert('I was clicked!')">Click Me...</span>
The result would be this:
Click Me...
We can also move a portion of the script to the dedicated script area by calling the function from the attribute value:
<script type="text/javascript">
function react(){alert('I was clicked!');}
</script>
<span onclick="react()">Click Me...</span>
Luckily, we know that we can address elements around the page using their
id attribute, which means we don't have to assign specific attributes when we're working on the markup, we just have to know that the element will be the target of event handlers, and give it an id value:
<span id="abc123">Click Me...</span>
<script type="text/javascript">
var clicker = document.getElementById("abc123");
clicker.onclick = function(){
alert('I was clicked!');
};
</script>
The result would be exactly the same:
Click Me...
Note that the script appears after the element within the document. This is because if it were to appear before it, it would attempt to assign the
onclick property to an element that doesn't yet exist within the document. The common solution is to assign all event properties after the page has been loaded, by applying the onload event handler attribute to the BODY element of the page. This will be discussed later.So what are the common events that can be expected to be triggered by most elements on the page? Any 'native' HTML/XHTML element that has a visual representation on the document will react to the following events:
onmousedown- Mouse button was pressed.onmouseup- Mouse button was released.onclick- Mouse button pressed and released. Event triggered after the release.ondblclick- Mouse double-click.onmouseover- Mouse pointer entered element's area.onmouseout- Mouse pointer left element's area.onmousemove- Mouse pointer moved while over the element.
function reaction(){
alert('I was clicked!');
}
myElement.onclick = reaction;
Note that there were no parentheses after the function name when it was handed to the event handler.
At this point, I'd like to give an example of using the basic event model along with a DOM script. If you haven't had experience with DOM scripts before, the three-part introduction to DOM scripting should give you a solid foundation to everything you need to know to get started. You don't need to understand DOM scripting to move on, however. I'm placing this example here because, in conjunction with DOM scripting, even the basic event model may be quite powerful in a simple script.
Here's how to assign one event handle to multiple elements:
function reaction_on(referrer){
referrer.style.backgroundColor = '#DDD';
}
function reaction_off(referrer){
referrer.style.backgroundColor = '#FFF';
}
var anchors = document.getElementsByTagName("a");
for(var i=0; i<anchors.length; i++){
anchors[i].onmouseover = function(){reaction_on(this);}
anchors[i].onmouseout = function(){reaction_off(this);}
}
The script above seeks out all of the
A elements within the document and adds onmouseover and onmouseout event handlers to them. In this case, it gives them a light grey background color when hovered over, clearly a job for CSS, but the idea is that you can loop through the entire document and look for just the elements which you want to add handlers to (and not just one type of element -- if you're familiar with DOM scripting, you know you can be much more specific and achieve much more with this type of methodology).The onload Event
As you can guess by now, it's rather frustrating placing all of the event-handling code after the elements you want it to effect have been loaded into the document. One simple solution is to add theonload event handler to the BODY tag:
<body onload="runStuff()">
Of course, while this works, it isn't very portable. That is, it won't allow you to just include a .js file into a page and have it take effect without any other modifications the way you'd like it to, since you'd have to add this event handler to the
BODY element, and you'd have to know which one to add. Luckily, we can script our way to a solution:
window.onload = runStuff;
Again, notice that there are no parentheses after the function name. So why is the
onload event handler being added to the window instead of the BODY? Well, because if your script is in the head of the page, the BODY has yet to appear in the document. Make no mistake, however, this is exactly like adding event handlers to the BODY element -- it's specifically designed as a work-around to this problem.If the page you're dealing with was designed by you, then this should suffice. You can stack functions in the
runStuff() function in the order in which you want them, and all is well. However, what if you don't know if there are already event handlers assigned to the BODY? You're assigning a completely new function to the onload event handler, which means that anything that was previously there is being overwritten. Fear not, there's even a solution to that scenario:
function appendLoadEvent(newFunc)
{
var oldonload = window.onload;
if (typeof window.onload != 'function'){
window.onload = newFunc;
} else {
window.onload = function(){
oldonload();
newFunc();
}
}
}
appendLoadEvent(runStuff);
This way, you're not overwriting the old function, but rather appending the new one to a list of functions that are to be triggered when the page finishes loading. There are additional ways to do this, which allow you to order the functions, but this is generally enough functionality which is supported in relatively older browsers.
Event Handling with DOM Level 2
Advanced event handling in DOM Level 2 gives you considerably more control over events that occur within the document. As the first example of what this type of specificity refers to, you can actually create something using DOM Level 0 event handlers:
<style type="text/css">
#block_yellow
{
display:block;
width:250px;
border: 1px solid #FF9900;
background-color:#FFCC00;
padding: 15px;
}
#block_green
{
display:block;
border: 1px solid #009900;
background-color:#00CC00;
padding: 15px;
}
#block_blue
{
display:block;
border: 1px solid #0099FF;
background-color:#00CCFF;
padding: 15px;
}
</style>
<div id="block_yellow">
<div id="block_green">
<div id="block_blue">Some Text</div>
</div>
</div>
<script type="text/javascript">
document.getElementById("block_yellow").onclick = function (objEvent) {
alert("block_yellow was clicked");
};
document.getElementById("block_green").onclick = function (objEvent) {
alert("block_green was clicked");
};
document.getElementById("block_blue").onclick = function (objEvent) {
alert("block_blue was clicked");
};
</script>
Here's what this code produces:
Some Text
Click the middle rectangle.
Three events were triggered, one after the other. In effect, you clicked a point in the document that was handled by three separate event handlers, since when clicking the middle rectangle, you were also within the area of the green rectangle, and the yellow rectangle. This is an element of the DOM Level 2 Event Model which is called the Event Flow. An event is triggered both by an element, and by every ancestor of that element, in a certain order. The order in which these events take place is split into two stages: First the The Capture Phase, and then the Bubbling Phase.
When an event is pinpointed to be triggered by an element (for example, when you clicked the middle rectangle), the Capture Phase goes into action. It tells every ancestor element to fire an event, from the top-most element, to the most specific element that is the focal point of the event. So if you clicked on a link within the page, which was contained within a
SPAN element, then the Capture Phase would make the order of elements to fire events be: The document, the BODY, the SPAN element, and lastly the link. This doesn't mean that each of these elements has an event handler, but if it does, then that event handler will be triggerd in that order. This is exactly the same as the three rectangles above: elements nested within elements, each of which may have an event handler.But something doesn't make sense here -- When you clicked the middle rectangle, it fired the first event, followed by the green rectangle, and then the yellow rectangle. This is the opposite of the Capture Phase, since it fired the most specific element first, followed by the containing elements, in turn. The reason for this is that the events were assigned using the DOM Level 0 methods, which assign them to the Bubbling Phase of the event flow: The second phase. The Bubbling phase is named so because events bubble 'up' the document tree, firing from the most specific element, up to the top-most element, in that order.
At this point, a visual representation would be helpful:

Basically, if event handlers were assigned to each and every element, both to the Capture Phase and the Bubbling phase, then events would occur in the order you see above.
The Big Event Caveat
At this point, it would make sense for me to show you how to assign event handlers to both the Capturing Phase and the Bubbling Phase. The only problem is, the Internet Explorer Event Model does not support the Capture Phase, only the Bubbling Phase. There are work-arounds to this, but there's too much material to cover in this specific document. Therefor, since Internet Explorer versions 6 and 7 make up the majority of browsers in use today, I'll stick to the Bubbling Phase alone in this article.The DOM Methods
The DOM calls event handlers: "event listeners". Generally, the standard way of referring to something would become the common way of addressing it, except that people have been using the term 'event handling' for many other purposes, in many other models, for far too long. Thus, the term commonly used to describe the function that reacts to an event is still referred to as the "Event Handler". The methods defined by the DOM, however, are named by the standard, and in the standard the method to add an event handler is calledaddEventListner().The
addEventListner() method accepts three arguments: The name of the event, the name of the function to call, and a boolean value. That boolean value determines if the function should be called during the Capture Phase (boolean value 1), or the Bubbling Phase (boolean value 0). As mentioned, Internet Explorer does not have a Capture Phase, so for portability, we'll leave that boolean value at 0, which means that the event is called duing the Bubbling Phase. The event name is also a bit different: If you look at the list of common events above, you'll see that each of them begins with "on". The DOM omits the "on" part of the event name, so that 'onclick' becomes 'click', 'onmouseover' becomes 'mouseover', etc..Here's how you would assign an event handler using the DOM Level 2 model:
myElement.addEventListener("click", myFunction, 0);
This is similar to the basic event model's
myElement.onclick = myFunction;. In fact, it's the exact equivalent, with one exception: The DOM Level 2 method may add more than one function to the same element, whereas using myElement.onclick = ... will overwrite any previous function that was assigned to that event. You can intermix DOM Level 0 and DOM Level 2 methods in the same program, although it's not recommended, if only because it may introduce inconsistencies.Assigning more than one function to the same event is quite intuitive:
myElement.addEventListener("click", myFunction_A, 0);
myElement.addEventListener("click", myFunction_B, 0);
Of course, if you were to try to call run
addEventListner() in Internet Explorer, it would cause an error. What you therefor need to do is check if the method is supported before you use it:
if(document.addEventListener){
myElement.addEventListener("click", myFunction, false);
}
This conditioning makes sure that the
addEventListner() is indeed supported by the browser, whichever one it may be. Note that, again, there are no parentheses after document.addEventListner, since you're referring to the function by name, not invoking it. When we add the Internet Explorer event handlers later, we'll add the same conditioning for other browsers, so that we know that each function is assigned using the right model according to the browser.Another ability of advanced event handling is the ability to remove specific event handlers by calling
removeEventListener(). You can specifically remove an event handle of a particular event by specifying its name, while leaving other event handlers already assigned to that event intact, like so:
myElement.addEventListener("click", myFunction_A, 0);
myElement.addEventListener("click", myFunction_B, 0);
// other things occured...
myElement.removeEventListener("click", myFunction_B, 0);
Naturally, this code, again, would be conditioned, so that it would be guaranteed to at least not cause errors in Internet Explorer. I should mention at this point that I'll provide a more coherent way of dealing with adding and removing event handlers in a cross-browser fashion later, so that you don't have to condition each and every function call.
This ability, to specificy which functions to attach as event handlers and which ones to removed, by name, at any time, is very powerful. It may not be apparent at first glance the wide array of functionality this affords you, but it basically allows you to manage a 'list' of event handlers for each event of every element in the document. At your whim, you are able to make elements active or static, in many aspects, allowing you to create intricate application behaviour.
While there is still a lot to cover in regards to the DOM Level 2 functionality, I'd like to, at this point, introduce the Internet Explorer Event Model aquivalents, so we can begin constructing cross-browser functionality.
The Internet Explorer Event Methods
At this relatively basic level of event handling, there isn't much difference between the two models except the names of the methods:addEventListner() becomes attachEvent(), and removeEventListener() becomes detachEvent(). There are two differences between the DOM methods and the IE methods; The first is the amount of arguments they take: The Internet Explorer methods take two arguments, not three. This is because the Internet Explorer model only supports the Bubbling Phase, as mentioned above, so there's no reason for it to take a third, boolean argument -- the event handler will always be attached to the Bubbling Phase. The second is that IE methods do not omit the "on" part from the event name. This means that while 'onclick' becomes 'click' in addEventListner(), it remains 'onclick' in attachEvent().You would use it like this:
myElement.attachEvent("onclick", myFunction);
Here, as before, this will cause an error if launched in a browser which doesn't support the IE event model. So once again, we'll condition to prevent an error:
if(document.attachEvent){
myElement.attachEvent("onclick", myFunction);
}
Just like in the DOM event model, you can use this method to attach multiple handlers to a single event, and then use
detachEvent() to remove these handlers by name.There is, of course, a lot more to cover when dealing with the Internet Explorer Event Model, however now that we have some common grounds upon which to create cross-browser functionality, it's best to begin there. I'll gradually add additional features so that you'll have coverage on both models.
Initial Cross-Browser Advanced Events
So we know that the DOM usesaddEventListner() to add event handlers, and that IE uses attachEvent(). When removing events, these methods are removeEventListener() and detachEvent() for the DOM and IE, respectively. We also know that there's no simple manner with which to attach a Capture Phase event handler in IE, so we'll have to drop that functionality in the initial cross-browser implementation.If we were to just mix the code from the two models, we'd come up with this:
if(document.addEventListener){
myElement.addEventListener("click", myFunction, false);
} else if(document.attachEvent){
myElement.attachEvent("onclick", myFunction);
}
This is just the combination of the code we saw earlier for each of the two models. Actually, this works perfectly fine. Except, of course, for being very long when you're just trying to attach a single event.
The solution is to create a wrapper function that would sort things out for you. Something along the lines of:
function addHandler(whichEvent, whichObject, attachThis){
if(document.addEventListener){
whichObject.addEventListener(whichEvent, attachThis, 0);
}else if(document.attachEvent){
whichObject.attachEvent("on" + whichEvent, attachThis);
}
}
Now, when you want to attach an event handler to an element on the page, you'd simply write:
addHandler("click", myElement, myFunction);
This will basically hide the clutter of implementation-detection and "function routing" from your code, and allow you to focus on functionality. The only things to remember is that you use the event name without the "on" prefix (it'll be added automatically if required), and don't place a parentheses after your function name, since you're passing it as an argument.
At this point, it's worth noting that you'd normally see much larger, more involved wrapper 'classes', rather than stand-alone functions. Not that using stand-alone wrappers like this is bad in any way when this is all the functionality you need, but you'll see, as we cover more elements of the event models, there are many more features that each browser handles differently, and it makes more sense to consolidate all of the cross-browser 'abstraction layer' into one class. Still, I'll continue to display examples in short functions, so that that it's possible to see what the intention is at one glance, rather than gradually build one large class. In practice, check out various implementations on broad-reach sites (view-source is your friend) and choose the way that's most comfortable for you to handle this.
In Events: Part 2 I'll elaborate on controlling the Event Flow, additional cross-browser functionality, and capturing mouse pointer coordinates.
Return to the JavaScript section, or go the to Main page.