Cross-browser JavaScript and the Event Object

JavaScript is not a standard in the same way HTML and CSS are. This is changing. With every major browser release JavaScript is becoming more and more universal in it's implementation.

An example: innerHTML which this site has made heavy use of thus far was created by Microsoft around 1999. It was, of course implemented in Internet Explorer, but as it was outside the standard JavaScript implementation, codified by the W3C, the other major browsers of the time (Netscape Navigator and Opera) did not implement it. The result was years and years of writing two methods of amending data in a page, one for Internet Explorer and one for the other browsers. Over the years attitudes to innerHTML have softened and whilst it is still not officially a part of W3C JavaScript, every modern browser release can now be explected to implement it and in the same way.

This is not the case with every part of JavaScript!

The event object is still the biggest and most painful example of incompatible cross-browser JavaScript. Several event object models exist and are presently live in current version browsers.

Event object and event handlers

It isn't always an issue that event models differ between the major browser families. Most of the time event handlers can be used to simply trigger blocks of code as the examples in this site have so far attempted to do. The issue arises where there is need for the script to be made aware of which page element triggered the event handler.

A good example of code in which it is advantageous to know which element has triggered the event handler would be the draggable blocks on the BBC homepage. Each block performs similarly in it's dragability so to write code to individually make each block draggable would be wasteful. The code need to know which block is being dragged and where it ends up so it can re-arrange all the other blocks on the page accordingly. In this instance, to avoid using the event object, the coder would need to create function blocks to handle the movement of each block respectively (and that's a lot of code). It is far better to create one block of code to handle any given block being dragged and then, within that, to figure out which block is being dragged.

The Mozilla (Firefox, Opera, Chrome etc) model

In the Mozilla (W3C) model, the Event object is created each time an event handler is triggered and is then passed to whatever code the event handler calls.

Try the following code out for yourself:

<input type="button" name="eventButton" 
 value="Event Object Demo Button (Firefox)" id="ffButton" />
 
<script language="javascript" type="text/javascript">
    document.getElementById('ffButton').onclick = function(event) {
    	alert('You clicked the ' + event.target.value + ' button');
    }
</script>
 

Live example (FireFox / Opera etc, this will not work in IE)

The code above creates a named button with an ID of "ffbutton". The script block sets a new function to run when the onClick event handler of "ffbutton" is activated. This function runs an alert which reports the text displayed on the element causing the alert ("ffbutton").

Internet Explorer event model example

Internet Explorer is quite different. When an event is triggered in IE, a global event object (window.event is updated to reflect this.

Try the following code out for yourself:

<input type="button" name="eventButton" value="Event Object Demo Button"
 onclick="alert('You clicked the ' + window.event.srcElement.value)" />

Live example (IE, this will not work in anything other than IE)

The above code references the global IE window.event object and retrieves from it the text on the element which triggered the event, the button above.

Making this code work in all browsers...

... is tricky!

For years developers would use JavaScript to report the name of the browser being used using code like the following:

var myBrowser = navigator.appCodeName + " " + navigator.appVersion;

Live example

Your browser is.....

... but that quickly gets messy and the coder can easily find themselves writing the same code 3 time in different ways. This type of code is difficult to write, difficult to keep up to data. As JavaScript evolves newer browsers have greater functionality than older browsers. This means that as well as browser types, browser versions also needed to be detected and as soon as a new browser version came out, everyone using this technique needs to amend their code to take account of the range of JavaScript the newly released browser can use.

Writing elegant cross-browser event code

Rather than detecting which browser the client is using and then understanding what that browser is capable of it is far easier to test the browser's capabilities directly. In doing so we can work out which code model we need to use in any given instance. For example:

<input type="button" name="eventButtonGlobal" value="Global Event Button" 
  id="globalEventButton" />
  
<div class="exampleblock" id="browserVersion2">
	Nothing here at the moment.
</div>

<script language="javascript" type="text/javascript">
// set the event handler for the button above (this works 
// in all browser models) this sets the event handler to 
// trigger a function called reportMyBrowser
document.getElementById('globalEventButton').onclick=reportMyBrowser;
// define the reportMyBrowser function, as it is an event 
// handler it needs to be passed an event object in the 
// W3C model (IE will ignore this)
function reportMyBrowser(event) {
// declare an object for the event
	var eventObject;
// declare an object for the target 
// (The HTML object that called the event)
	var eventTarget;
// check which event model we are using by asking
// IF the window.event object is present:
	if (window.event) {
// then we are using Internet Explorer of some kind, 
// set eventObject to the global window.event object
		eventObject = window.event;
// and set eventTarget to the HTML element which 
// caused the event
		eventTarget = window.event.srcElement;
	}
	else {
// we are more than likely using something more 
// W3C compatible, set eventObject to the one 
// passed into this function
		eventObject = event;
// and set eventTarget to the HTML element which 
// caused the event
		eventTarget = event.target;
	}
// now we have a variable which refers to the target of the event 
// and in both models it is a HTMLElement object which will be 
// targetted.  Thus we can expect the target to behave consistantly 
// from this point.
// The set the HTML block to the text grabbed from the 
// clicked button.
	document.getElementById('browserVersion2').innerHTML = 
		"You clicked ... " + eventTarget.value;
}
</script>
Nothing here at the moment.

This looks a little bit cumbersome but the code can be simplified. By removing the comments and condensing the if / else block we can achieve this same result with the following:

<input type="button" name="eventButtonGlobal2" 
	value="Global Event Button Condensed" id="globalEventButton2" />
    
<div class="exampleblock" id="browserVersion3">
	Nothing here at the moment.
</div>

<script language="javascript" type="text/javascript">
document.getElementById('globalEventButton2').onclick = function (event) {
	var val = (event)? event.target.value : window.event.srcElement.value;
	document.getElementById('browserVersion3').innerHTML = 
		"You clicked ... " + val;
}
</script>  

Live example

Nothing here at the moment.

Further reading

For a fuller description of exactly what works and what doesn't in what browser have a look at the Quirksmode intro to events page.