Javascript - Working With Events
|
The simplest way to attach to an event for an element is to write the javascript call straight in inline in html code - and this also happens to be the most compatible way. Often, the inline code just calls a function defined elsewhere. For example, here, when this div element is clicked, the javascript function myFavoriteFunction is executed.
|
<div onclick="myFavoriteFunction();"></div>
A less common use is to actually write multiple javascript statements directly into the html. Here, for whatever reason, we are executing two
alert calls when the div is clicked.<div onclick="alert('Message 1'); alert('Message 2');"></div>
The next most common way to attach events is to attach them directly to the element object in javascript. In the following code, we obtain an element object using
getElementById, and then attach the myFavoriteFunction method to the onclick event by setting the onclick property of the element object:var element = document.getElementById("myFavoriteElement");
element.onclick = myFavoriteFunction;
element.onclick = myFavoriteFunction;
Note that the line is
element.onclick = myFavoriteFunction, not element.onclick = myFavoriteFunction(). This is because we are setting onclick equal to the function itself, not the result of the function. If the parentheses are there, the function would be evaluated.This technique can also be used to remove an event - by setting that property back to null:
var element = document.getElementById("myFavoriteElement");
element.onclick = null;
element.onclick = null;
These methods of working with events are supported by every browser, but they leave a little bit to be desired. The major flaw: attaching multiple callback functions to an event. Fortunately, there are more advanced methods of attaching and detaching events. But like almost everything else in the world of html and javascript, the techniques differ slightly between browsers.
For most browsers following the W3C standards, the function
addEventListener is used for attaching events, and the function removeEventListener is used to detach them. They work in the following manner:var element = document.getElementById("myFavoriteElement");
element.addEventListener("click", myFavoriteFunction, false);
element.removeEventListener("click", myFavoriteFunction, false);
element.addEventListener("click", myFavoriteFunction, false);
element.removeEventListener("click", myFavoriteFunction, false);
That seems pretty straightforward - except what does the last parameter to that function mean? That parameter is there because in the W3C standard there are two possible times that any event can be triggered. One is during the capture phase, and the other is during the bubble phase. The capture phase goes first - which is as an event travels downward through parent nodes to reach the final child destination node. The bubble phase is the opposite direction - as an event travels back upward from its destination node (again passing through all the parents). So, for instance, if you had an image in a table cell, which is in a table row, in a table, which is in on an html page, and you clicked on the image, the following would happen in the W3C event model. First, if anything is attached to the document onclick event for the capturing phase, it would fire. Then the table would be checked, and then the row, and then the cell, and finally the image itself. Anything attached to the onclick event for those elements, and set to go off in the capturing phase, will be fired. Then the event bubbles: the onclick for the image again would be checked, but this time for the bubbling phase. And then again with the table cell, and then the table row, the table itself, and finally the document. This gives you, as a developer, an extreme amount of flexibility as to when your event will fire.
And so how do you set during which phase you want your function to be called? When that third argument is true, the function is hooked for the capture phase, and if false, it is hooked for bubbling. Oh, and a useful thing to note is that all events set using the traditional techniques are set to fire during the bubbling phase.
But of course, Microsoft has its own set of standards. Which means that in Internet Explorer, there the functions to attach and remove events are different:
attachEvent and detachEvent. And, similar to the W3C methods, they are used in the following way:var element = document.getElementById("myFavoriteElement");
element.attachEvent("onclick", myFavoriteFunction);
element.detachEvent("onclick", myFavoriteFunction);
element.attachEvent("onclick", myFavoriteFunction);
element.detachEvent("onclick", myFavoriteFunction);
But what happened to that third argument? Well, in the Microsoft standard, events ever only bubble - there is no capturing phase. So there is no need for that third parameter. What this means as well is that writing a web page and relying on events firing during capture phase is impossible - because such a page will not work in Internet Explorer. Sad, really, because a lot of flexibility is squandered.
Now, we don't want to have to worry about these differences when we are writing javascript code - so it is generally best to hide these incompatibilities with a wrapper. And that is exactly what we have here:
function hookEvent(element, eventName, callback)
{
if(typeof(element) == "string")
element = document.getElementById(element);
if(element == null)
return;
if(element.addEventListener)
element.addEventListener(eventName, callback, false);
else if(element.attachEvent)
element.attachEvent("on" + eventName, callback);
}
function unhookEvent(element, eventName, callback)
{
if(typeof(element) == "string")
element = document.getElementById(element);
if(element == null)
return;
if(element.removeEventListener)
element.removeEventListener(eventName, callback, false);
else if(element.detachEvent)
element.detachEvent("on" + eventName, callback);
}
{
if(typeof(element) == "string")
element = document.getElementById(element);
if(element == null)
return;
if(element.addEventListener)
element.addEventListener(eventName, callback, false);
else if(element.attachEvent)
element.attachEvent("on" + eventName, callback);
}
function unhookEvent(element, eventName, callback)
{
if(typeof(element) == "string")
element = document.getElementById(element);
if(element == null)
return;
if(element.removeEventListener)
element.removeEventListener(eventName, callback, false);
else if(element.detachEvent)
element.detachEvent("on" + eventName, callback);
}
These two functions will attach and detach events regardless of the browser, and they come with an added bonus. They can take either an element object, or an element ID as input for the
element argument. For instance, they could be used in the following fashion:hookEvent("myFavoriteElement", "click", myFavoriteFunction);
var el = document.getElementById("myFavoriteElement");
unhookEvent(el, "click", myFavoriteFunction);
var el = document.getElementById("myFavoriteElement");
unhookEvent(el, "click", myFavoriteFunction);
Sadly, that is not where the differences between the standards end when it comes to eventing. First off, how to get the event object (the object that contains information about a fired event) is different between browsers. In the W3C standard, the event object is actually passed as an argument to the function callback. To get this event object passed when inlining the callback in html it needs to be in the following form:
<div onclick="myFavoriteFunction(event);"></div>
That
event argument lets javascript know to pass the event object to the function. For the other methods - setting the property on the element and or addEventListener/attachEvent, it is assumed that the function you are giving will take one argument, which will be the event.But with Internet Explorer, the event is not passed as an argument (except if it is passed in the inline form like above). Otherwise, it is only accessible as a property of the window:
var event = window.event;
This is pretty easily wrapped, and heres an easy way to do it:
function getEvent(e)
{
if(!e)
return window.event
return e;
}
{
if(!e)
return window.event
return e;
}
And here is how you can use it:
function myFavoriteFunction(e)
{
var e = getEvent(e);
//do stuff with the event
}
hookEvent("myFavoriteElement", "click", myFavoriteFunction);
{
var e = getEvent(e);
//do stuff with the event
}
hookEvent("myFavoriteElement", "click", myFavoriteFunction);
Of course, the event object by itself is not that useful - it is the contents that matter. For instance, you can get the target of the event from this object. But, yet again, the W3C and Microsoft models are different. For W3C it is the
target property, but for Microsoft it is the srcElement. Once again, it is pretty simple to write quick wrapper:function getEventTarget(e)
{
if(!e)
e = window.event;
if(e.target)
return e.target;
return e.srcElement;
}
function myFavoriteFunction(e)
{
var target = getEvent(e);
//do stuff with the target
}
hookEvent("myFavoriteElement", "click", myFavoriteFunction);
{
if(!e)
e = window.event;
if(e.target)
return e.target;
return e.srcElement;
}
function myFavoriteFunction(e)
{
var target = getEvent(e);
//do stuff with the target
}
hookEvent("myFavoriteElement", "click", myFavoriteFunction);
Another really useful thing to be able to do is cancel the event. This means that the event will stop propagating - i.e., nothing else on the capture or bubble chain will fire. Sadly, there are a lot of different ways to do this across browsers. The main ones are the property
cancelBubble for Internet Explorer (which you set to true if you want it to stop propagating) and the function stopPropagation for W3C compliant browsers. The function preventDefault will prevent default actions from happening in Firefox (like the Page Up key actually moving the page upward). The properties cancel and returnValue are listened to by various other browsers (and browser versions), and its just always good to return false out of an event function if you want to stop propagation. So heres a nice little wrapper that will do all that for you:function cancelEvent(e)
{
if(!e)
e = window.event;
if(e.stopPropagation)
e.stopPropagation();
if(e.preventDefault)
e.preventDefault();
e.cancelBubble = true;
e.cancel = true;
e.returnValue = false;
return false;
}
function myFavoriteFunction(e)
{
//do stuff
return cancelEvent(e);
}
hookEvent("myFavoriteElement", "click", myFavoriteFunction);
{
if(!e)
e = window.event;
if(e.stopPropagation)
e.stopPropagation();
if(e.preventDefault)
e.preventDefault();
e.cancelBubble = true;
e.cancel = true;
e.returnValue = false;
return false;
}
function myFavoriteFunction(e)
{
//do stuff
return cancelEvent(e);
}
hookEvent("myFavoriteElement", "click", myFavoriteFunction);
And that is it for this tutorial on working with javascript events. There are a number of other things in that event object, such as mouse position and keys pressed - but that is a topic for another day. If you have any questions about how events are supposed to work, feel free to leave them in the comments.
Posted in Javascript, All Tutorials by The Tallest |

August 28th, 2007 at 9:49 pm
Wonderful article. I enjoyed it.
Thanks
Aamir
October 22nd, 2007 at 7:52 am
Thanks for the examples!
January 13th, 2008 at 11:00 am
very nice, informative article, I have a one query regarding Javascript Event,
in one the condition I have one Span tag inside a TD tag of and i already registered onclick event of td. it work fine in firefox but in IE when event fires i get srcElement either Span or TD element depends on click position. but actually i always need TD element. do you have any solution for this.
January 13th, 2008 at 5:02 pm
Yeah, this is a known problem in the Microsoft model. If you want to, you can read more about it here.
The solution that I generally use in cases like yours is to check the tag name. If the tag of the srcElement is “SPAN”, look at the parent element of srcElement. Otherwise, just look at the srcElement.
March 10th, 2008 at 11:01 pm
Why would one want to cancel a event after putting it there. Can you please explain using a practical example?
March 26th, 2008 at 8:15 am
superb article, i really enjoyed using it.
excellent work
May 19th, 2008 at 3:12 am
all the tutorials are really niece and good.
great job!
July 1st, 2008 at 2:15 pm
Thank you for this tutorial! It will help me a great deal. Very complete.
I have just discovered this site, and I will return often.
You have done a great job.
Fernando
August 1st, 2008 at 5:47 pm
Terrific article — thanks so much for the comprehensive look!
October 23rd, 2008 at 11:13 am
Hello,
Thanks for the article. I have one question, though. What if I want to attach a function that has parameters to an event? In all of the examples provided, the sample function being attached to the ‘onclick’ event does not have any parameters passed in.
When I try use ‘myfunct(param1, param2)’, it seems like the function gets executed, which is not what I want to happen.