Unobtrusive Image Rollovers with Low Pro
February 17th, 2008
This week I finally broke down and decided to find out what this Unobtrusive Javascript thing is all about. First, I got really excited about trying out the UJS plugin for Rails. I’m not linking to it, because after a couple of hours of digging around, Googling for the errors I was getting and finally digging into the source to find out it was breaking the tag_options helper in Rails 2, I discovered it’s no longer a supported plugin. Dan Webb, the original author, came to the conclusion that the unobtrusive message was not getting through to the typical Rails developer who picked up the plugin for it’s shortcuts. He pulled the lowpro.js library out of the plugin and ditched the Rails bits. I think it was a good move (I just wish someone would update ujs4rails.com to say something to that effect – it’s still the same page it was over a year ago, and it really sells the plugin well…)
There’s not really a lot of documentation for Low Pro out there, but to be honest, once you start using it I think you find there really isn’t a lot to it. Built on prototype, the lowpro.js file is only 300 or so lines of code, including some helpful comments. There are some links on the lowpro wiki to a couple blog posts with examples. In particular I found Jarkko Laine’s article very helpful and of course Dan’s blog is worth a read. Unfortunately, due to the speed of technology, some of Dan’s examples are a little dated (thanks to improvements he’s made to the code).
So I figure, how’s about another example? One key concept behind unobtrusive javascripting is yanking those obnoxious mouse events out of your anchor tags; Onmouseover, onmouseout, etc – they’ve got to go. So how can we use Low Pro to add a rollover behavior to some images?
note: at the time of this writing, Low Pro is version 0.5 and requires prototype version 1.6.x
Step 1: Plain HTML
Let’s start with the HTML. Imagine a menu that’s just a column of images; no javascript yet, just plain ol images (and of course styled with CSS in another file):
1 2 3 4 5 |
<ul class="main_menu"> <li><a href="/pages/home"><img src="home.gif" alt="Home"/></a></li> <li><a href="/pages/about"><img src="about.gif" alt="About"/></a></li> <li><a href="/pages/contact"><img src="contact.gif" alt="Contact"/></a></li> </ul> |
Very simple. Probably everyone knows the mechanics of a rollover by now: what we want is for the img src attribute to change to a different value when the mouse cursor is placed over the image. The effect is getting a different image to display in place of the current image. And of course, when the cursor leaves the area of the image, we want the src to switch back. So how do we do that without using our tried and true “onmouseover/onmouseout” HTML attributes?
Step 2: Create a Behavior
We want to define a Behavior to describe the effect certain mouse events have on a particular image object. Low Pro makes this nice and clean, using the Class module baked into prototype to give us an object-oriented feel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
var ImageRollover = Behavior.create({ // define some attributes for this new Behavior (mmmm, OOP...) orig_name: null, // the filename without extension image_ext: null, // jpg, gif, etc, plus any ?123 nonsense at the end roll_suffix: null, // whatever we want to stick on the end of the image name // one of these instances of ImageRollover will be created for each image // when we set them up, we'll need to know the "suffix" of the rolled over image // for example, home.gif might be our image and home_on.gif might be our // rollover, making '_on' our suffix initialize: function(roll_suffix) { this.roll_suffix = roll_suffix // here comes some lovely regex // image.src example: http://example.com/images/something.gif?1234567890 // we need to chop that up into: // 'http://example.com/images/something' and '.gif?1234567890' matches = this.element.src.match(/(.*)((?:.gif|.jpg|.png).*)/); this.orig_name = matches[1]; this.image_ext = matches[2]; }, // this function will get called on mouseover onmouseover: function() { // we're just stapling the pieces back together with the suffix between // the file name and the file extension this.element.src = this.orig_name + this.roll_suffix + this.image_ext }, onmouseout: function() { // back to the original, no suffix this.element.src = this.orig_name + this.image_ext } }); |
So there’s our behavior. It’s nice and generic – we could apply it to any image element. I’m not great with regex, but this will work for any image that ends with .gif, .jpg, or .png and optionally if it has an asset timestamp at the end following a question mark (Ruby on Rails likes to stick these on the end of assets to trick browsers into reloading cached image, css, and js files that might have changed when code has been updated). I recently came across this great testing tool for regex expressions: http://www.rubular.com/. Of course, the Firebug console is also great for this type of thing as well. For some reason my regular expression yields a third, unnecessary match (just the file extension with no timestamp) and I can’t quite nail it down. It doesn’t affect anything, but hey – let me know if you have a suggestion. Edit: Andy (in the comments) points out where the extra match comes from and how to ignore it. I updated the regex and it works.
Step 3: Assign Behavior
Finally, we can assign our ImageRollover behavior to some images. The magic of CSS selectors makes it easy to assign a behavior to a whole group.
1 2 3 |
Event.addBehavior({
'.main_menu img': ImageRollover('_on')
}); |
The addBehavior method takes a hash where every key is a CSS selector and the value is a behavior. In this example, we can see that the CSS selector is saying “look for elements of the main_menu class and collect any of their children that have the img tag”. We also have to pass a value to ImageRollover that designates the rollover image suffix needed by the initialize method of the ImageRollover class.
Want proof that it works? See it in action (warning: extremely bad graphics that I cranked out in 5 minutes).
So there you have it. If anyone who knows Low Pro has any feedback on improving this example, I’d love to hear it.
3 Responses to “Unobtrusive Image Rollovers with Low Pro”
Sorry, comments are closed for this article.


February 18th, 2008 at 10:37 AM The third match is coming from this:
(.gif|.jpg|.png)In Perl you can use (?: ) to not store this match. Not sure about JS, but I would imagine it’s similar if not the same. :)
February 18th, 2008 at 12:17 PM
Ah, of course – I thought that might be where it was coming from, but didn’t know how to make it not store. Thanks, Andy. I updated the expression in the code.
April 15th, 2008 at 09:06 AM
A bit off-topic, but you mentioned an “asset timestamp at the end following a question mark”, and I’m having a problem exactly with that. In the development environment it runs just fine, but in production it doesn’t find the asset (and thus pages are left unstyled). Do you know what could be the problem? Thanks a lot!