“Brief” Guide to JavaScript – Part 6
Events & Handling
In conjunction with the DOM tree, events are an important aspect of the browser-provided functionality. Think of events as a "reaction" to a particular situation. For example, when the user clicks, you may want to do something. Events provide this capability.
Some events browsers provide:
- load: When the page is finished loaded. Applies only to the window object.
- unload: When the user is attempting to leave the page. Applies only to the window object.
- click: When the object is clicked on by the user.
- dblclick: When the object receives a double click by the user.
- keyup: When the user releases a key. Applies only to window, body, input and textarea.
- keydown: When the user presses down on a key. Applies only to window, body, input and textarea.
- mouseup: When the user releases a mouse button down on the object.
- mousedown: When the user presses a mouse button on the object.
- change: When the input data changed (after the user no longer has the element focused). Applies only to input and textareas.
- focus: When the user provides input focus. Applies only to input, select, and textareas.
- blur: When the user leaves the input focus. Applies only to input, select and textareas.
- resize: When the user resizes the browser window that contains your page. Applies only to window object.
- submit: When the user submits a form. Applies only to form, not submit button.
The load event is useful to access elements not available to your script (html code after your script tag), but there's a flaw which we'll cover in a bit. Normally you would use load if you script is in the head of your document.
Attaching Code to Events
The simplest way to add your own functionality when an event occurs is to set the property with on<eventname>:
// window is prefixed for clarity--we're attaching an event // to something versus just setting some random variable. window.onload = function(){ alert('page loaded!'); };
So when the page finishes loading, an alert is displayed. Quite similar to those attributes you've seen in html documents with on<eventname>="<javascript>". Like <a href="http://google.com" onclick="alert('you\'re leaving my site!')">Google</a>, although embedding JavaScript isn't part of HTML’s philosophy—to represent only data.
The methods above are not ideal. Mostly because only one function can execute per assignment (every element and its events).
window.onload = function(){ alert('I never get executed'); }; window.onload = function(){ alert('hello world!'); };
Now if you only intend to have one function call for a particular element's event, that maybe fine for you, but as you write more JavaScript, or write your own framework, this isn't ideal. The solution is to attach functions to events with browser-provided functions. There's the w3c-compliant way, and the IE way.
- element.addEventListener(type, function, capturing) [w3c-compliant]: Calls function whenever the given event type (as a string) occurs. Capturing should always be false to utilize event bubbling (which is the only kind of event propagation IE supports.)
- element.attachEvent(type, function) [IE-specific]: Calls function whenever the passed event type (as a string) occurs. Type should be prefixed with 'on'. Only supports event bubbling.
Capturing & Bubbling
There are two kinds of event propagation styles since parent elements also receive the event. This is the preferred method most of the time, especially since IE doesn't support event capturing. But for those curious, I'll briefly explain the difference. The first is event capturing:
Here we're assuming that we caused an event to occur on Element 2 (eg - we clicked on Element 2). The event handlers would fire be called in Element 1, then Element 2.
Then there's event bubbling:
Here the event handlers are fired in Element 2 before Element 1. To reiterate again, IE only supports this event propagation technique.
Event Object
The functions you use for event has a special event object it can access. As usual, there's the "all non-IE browsers" method and the IE method to get to this event object.
The normal method is the event object is passed to the function as the first parameter, while IE stores the event object in the global variable. Unfortunately, IE's event object is a bit different from other browsers.
For most browsers, the functions defined for events are as follows:
function clickHandler(evt){ // 'this' is a special variable that contains the HTML element that fired it. // This only works when using addEventListener or attachEvent. // We'll talk about 'this' in a future advanced topic. alert(this.innerHTML); // alternatively, you can do this: alert(evt.target.innerHTML); }
Where as IE would be something like:
function clickHandler(evt){ // contains the targeted element, we want to default to document // in-case IE wasn't called by through an element. var target = evt.srcElement || document; alert(target.innerHTML); }
You may have noticed we utilizes the OR expression oddity to our advantage. This ensures we are acting on an element (srcElement or document if srcElement does not exist).
The event object also provides some addition information, such as which keys were pressed and mouse position, but may not be available for all events:
- evt.keyCode, evt.which (older browsers): Provides the keyCode which the key was pressed.
- evt.pageX, evt.pageY (non-IE) and evt.clientX, evt.clientY (IE): mouse x & y positions of the mouse.
There are more, but those are the most common.
Browser Specificity
As you can see, we need to write code specific to various browser differences. We can easily do this by checking if the given function is defined. Any browser that doesn't support that function will simply have undefined (since all functions we want to access are usually through objects), so we can use a simple if statement block:
var elem = document.getElementById('mylink'); function clickHandler(){ alert("You've clicked me"); } if(elem.addEventListener){ // check for the standard event handler elem.addEventListener('click', clickHandler, false); } else { // do the IE method elem.attachEvent('onclick', clickHandler, false); }
Usually you'll want to abstract this into a function:
function addEvent(elem, type, func){ if(elem.addEventListener) elem.addEventListener(type, func, false); else elem.attachEvent('on'+type, func); } // example: addEvent(document.getElementById('mylink'), 'click', function(){ alert('hello');});
Of course we can do that for anything else like the different properties of events, but you get the idea.
Note: Using the "feature-detection" is recommended instead of detecting the browser to determine what to do.
Stopping Event Propagation
The browser has default behaviors for certain elements, such as going to new url when a user clicks on a link, or a submit input button passing form information to the given action page. Sometimes we want to stop that from happening.
One way is to stop event propagation.
As per usual, this varies by browser *cough* IE *cough*. For most browsers, you call the stopPropagation method for the event object:
function myEventHandler(evt){
evt.stopPropagation();
}
For IE, set the cancelBubble property of the event object to true.
function myEventHandler(){ window.event.cancelBubble = true; }
If we attached this function as a click handler to a link, the user would no longer be transferred to a new url when clicked on. Pretty handy for having links that do web app functionality or stopping a form from submitting when there are errors (eg - Empty required field).
If you want event propagation to still occur, but stop the browser default, you can utilize preventDefault (and returnValue property for IE):
function myEventHandler(evt){ var evt = evt || window.event || document; if(evt.preventDefault) evt.preventDefault(); evt.returnValue = false; }
Removing Attached Events
Another benefit of using addEventListener and attachEvent is the possibility of removing previously attached functionality to events. This is utilized by removeEventListener and detachEvent which accept the same kind of parameters as their attaching counterparts.
The only difference is that the function passed should be the exact same function that was used to attached it.
// for simplicity, this code only works for w3c-compliant browsers: function stopOnce(evt){ evt.stopPropagation(); // can't do whatever evt.target.removeEventListener('click', stopOnce, false); } document.getElementById('mylink').addEventListener('click', stopOnce, false);
For the given ID-ed link, the user will have to click twice to get to the href location the link refers to. A nice way to annoy your users.
Alternatively, if you like anonymous functions, you can use the special arguments variable:
document.getElementById('mylink').addEventListener('click', function(evt){ evt.stopPropagation(); evt.target.removeEventListener('click', arguments.callee, false); }, false);
I hate sounding like a broken record, but arguments is an advanced topic we'll cover.
Next time, we’ll cover some utility functions and regular expressions. Read part 7.