JavaScript Event Sheets
Every now and again, I find myself wishing that I could use
something like the CSS selectors to apply some JavaScript to a load of
different elements at once. For example, imagine that you had a handful
of images that you wanted to implement rollovers on*.
Commonly, you might have an image called, say, "foo.png", and the
rollover image called "foo_on.png". With JS rollovers you need to
attach an onmouseover and
onmouseout handler to each image. If you've only got one
image then you might do it like this:
<img src="roll.gif" id="rollfirst"
onmouseover="document.getElementById('rollfirst').src='roll_on.gif'"
onmouseout="document.getElementById('rollfirst').src='roll.gif'">
which would look like this:
If you need more than one image on a page to roll over, then a
pretty common technique is to name your images specially (as I have
done; the rollover version of myimage.gif is
myimage_on.gif) and then have
rollover and rollout functions which know how
to "generically" roll over and roll out of an image:
<script type="text/javascript">
function rollover(imgID) {
// get the image object we're referring to
var thisimg = document.getElementById(imgID);
// and add "_on" to its src
thisimg.src = thisimg.src.replace(/(\.[a-z0-9]+)$/i,'_on$1');
}
function rollout(imgID) {
// get the image object we're referring to
var thisimg = document.getElementById(imgID);
// and remove "_on" from its src
thisimg.src = thisimg.src.replace(/_on(\.[a-z0-9]+)$/i,'$1');
}
</script>
. . .
<img src="roll.gif" id="roll"
onmouseover="rollover('roll')"
onmouseout="rollout('roll')">
<img src="roll2.gif" id="roll2"
onmouseover="rollover('roll2')"
onmouseout="rollout('roll2')">
which would work like this:
But this still involves adding something (whether it's a load of JavaScript or a call to a neat little encapsulated function) to every image that needs to roll over. Wouldn't it be nice if you could apply a JavaScript event handler to images, say, based on their class, with a CSS selector? So you'd want to do something like:
img.rollover {
mouseover: rollover_handler;
mouseout: rollout_handler;
}
. . .
<img src="roll.gif" class="rollover">
<img src="roll2.gif" class="rollover">
Like this, in fact:
The bit above which says:
img.rollover {
mouseover: rollover_handler;
mouseout: rollout_handler;
}
is part of a JavaScript Event Sheet. Essentially,
it looks like a stylesheet, but instead of defining style properties on
elements indicated by the selector, it defines event handlers. Inside a
block of declarations, you specify an event that can occur on the
object (common examples would be click,
mouseover, mouseout) and then the name of a
JavaScript function which should be attached as the event handler for
that event to all elements indicated by the selector.
"But who cares?", I hear you crying. "JavaScript-based image rollovers are lame anyway!"
True enough. But you can attach all kinds of things rather
conveniently here. Most of my unobtrusive DHTML scripts have a big
section, done on document load, that walks through the DOM and attaches
event handlers to specified elements. This would make that a lot
easier; just add a JSES sheet* with, say,
ul.aqtree3 in it to do the binding of event handlers.
How to do it
JSES sheets are linked to a page in the same way as CSS stylesheets: with a LINK tag. Just add a tag like:
<link rel="jses" href="test.jses">
to your pages. You also need to include the Javascript library to make it work, and the WebFX XmlExtras library:
<script src="jses.js" type="text/javascript"></script>
<script src="xmlextras.js" type="text/javascript"></script>
Limitations
There are a few limitations with this technique.
- Browser support
- Because we have to go and fetch the external stylesheet ourselves, we need a browser that supports making requests for URLs from JavaScript. That's IE or Mozilla, currently. The Xml Extras library covers the differences between them, but browsers without some analogue to the XMLHttpRequest object just plain won't work.
- Not inline
- You can't embed JSES sheets into your HTML; they have to be
external and linked with the LINK tag. This is because there's no
<jses>tag like there are<style>and<script>tags, and we can't just make one up. - No remote hosting
- The JSES sheet is retrieved by JavaScript, and therefore must be hosted on the same domain as the HTML page, owing to JavaScript's security restrictions.
Credits
As with a lot of this stuff, it was built by standing on the shoulders of giants. In particular, WebFX's XML Extras library and Simon Willison's getElementsBySelector were crucial. Thanks, guys!
Updated 2006-01-13 to use the corrected addEvent function.
Stuart Langridge, November 2003