If you know JavaScript but have yet to make the leap to unobtrusive JavaScript, read on. I'll demonstrate how to upgrade a traditional DHTML rollover to an unobtrusive script and then to a script that leverages the Yahoo! User Interface Library's (YUI).
Rather than hardcoding event handlers as HTML attributes, unobtrusive JavaScript assigns them using the DOM. Well-structured, valid HTML is a prerequisite to DOM scripting. Documents should declare a DOCTYPE, they should validate against the DOCTYPE, and presentational styles should be applied with CSS (read "should" as "life will be easier for everyone if you do"). Use a few targeted CSS ID and class selectors to define page layout. Don't go overboard on defining selectors, remember, descendent selectors are your friend.
I'll use a simple text-based rollover for demonstration purposes. In a perfect world, with perfect browsers, this effect would be created using the CSS pseudo hover class but, as you've probably guessed, it doesn't work in Internet Explorer 6. Here's a screenshot of what we're building.

First, the style sheet. Nothing wrong here as long as these are stored in an external CSS file.
#menu { font: 85% Arial, Helvetica, san-serif; } #menu .btn { background-color: #e7e7e7; border-color: #999; border-style: solid; border-width: 2px; color: #666; float: left; margin: 0 5px; width: 150px; } #menu .btn a { color: #fff; background-color: #999; display: block; padding: .3em .5em; text-decoration: none; } #menu p { padding: 0 .5em; }
Next the markup. The old school technique involves writing the rollover script functions and triggering them from event attributes. More complex actions required more and more stuff muddying up what was a perfectly readable HTML document.
<div id="menu"> <div id="btn1" class="btn" onmouseover="rollOver('btn1')" onmouseout="rollOut('btn1')"> <a href="#">Link 1</a> <p>Description for link 1</p> </div> <div id="btn2" class="btn" onmouseover="rollOver('btn2')" onmouseout="rollOut('btn2')"> <a href="#">Link 2</a> <p>Description for link 2</p> </div> <div id="btn3" class="btn" onmouseover="rollOver('btn3')" onmouseout="rollOut('btn3')"> <a href="#">Link 3</a> <p>Description for link 3</p> </div> </div>
Finally, the JavaScript. A function to set styles on mouseover and another to restore the original styles on mouseout. If you change a color in the stylesheet, you'll have to change it here too.
<script type="text/javascript"> // Apply rollover styles function rollOver(el) { // Get the elements var thisBtn = document.getElementById(el); var thisLink = thisBtn.getElementsByTagName('a'); // Change their styles thisLink[0].style.backgroundColor = '#666'; thisBtn.style.borderColor = '#666'; thisBtn.style.color = '#000'; } // Reset styles function rollOut(el) { // Get the elements var thisBtn = document.getElementById(el); var thisLink = thisBtn.getElementsByTagName('a'); // Change their styles back again thisLink[0].style.backgroundColor = '#999'; thisBtn.style.borderColor = '#999'; thisBtn.style.color = '#666'; } <script>
Let's clean things up a bit. Strip those event handlers from the button containers.
<div id="menu"> <div class="btn"> <a href="#">Link 1</a> <p>Description for link 1</p> </div> <div class="btn"> <a href="#">Link 2</a> <p>Description for link 2</p> </div> <div class="btn"> <a href="#">Link 3</a> <p>Description for link 3</p> </div> </div>
Now, make a few CSS adjustments and additions to define the hover styles where they belong, in the style sheet.
#menu { font: 85% Arial, Helvetica, san-serif; } #menu .btn, #menu .btn-over { background-color: #e7e7e7; border-width: 2px; border-style: solid; color: #666; float: left; margin: 0 5px; width: 150px; } #menu .btn { border-color: #999; } #menu .btn a, #menu .btn-over a { color: #fff; background-color: #999; display: block; padding: .3em .5em; text-decoration: none; } #menu p { padding: 0 .5em; } /* Rollover styles */ #menu .btn-over { border-color: #666; color: #000; } #menu .btn-over a { background-color: #666; }
With that done, hook it all up with the script.
<script type="text/javascript"> // Attach event listeners to the buttons function findButtons() { var menu, btns, i; // Get the menu element to access the btn divs menu = document.getElementById('menu'); btns = menu.getElementsByTagName('div'); for (i=0; i<btns.length; i++) { // Attach event listeners for rollovers to the btns if (/btn/.test(btns[i].className)) { btns[i].onmouseover = function(){roll(this);}; btns[i].onmouseout = function(){roll(this);}; } } } // Attached to the buttons and ready to roll function roll(o) { // btn is our off state, so turn it on if (o.className == 'btn') { o.className = 'btn-over'; // Otherwise, turn it off } else { o.className = 'btn'; } } // Initialize the rollover window.onload = function() { findButtons(); } </script>
Everything is now in its proper place. No more JavaScript in the HTML and styles aren't defined in the JavaScript. This will be much easier to maintain.
YUI is one of the many JavaScript libraries that have gained attention in recent years. Some say it's too big and bulky and that may be true in certain cases, like creating a simple rollover. But it's rare in our Web 2.0 world that a site's only JavaScript-powered feature is a rollover. YUI is my current JavaScript framework of choice because along with being feature-rich, it's well documented, tested, and supported.
So here's the rollover script powered by the YUI's Yahoo! Global Object, and DOM and Event extensions.
<script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo-dom-event/yahoo-dom-event.js"></script> <script type="text/javascript"> // Create a menu object, put everything we need into it var menu = { // Initialize the menu rollover init : function() { // Get the btn elements btns = YAHOO.util.Dom.getElementsByClassName('btn', 'div', 'menu'); // Assign event listeners to the btns for (var i=0; i<btns.length; i++) { YAHOO.util.Event.addListener(btns[i], 'mouseover', menu.roll, i); YAHOO.util.Event.addListener(btns[i], 'mouseout', menu.roll); } }, // First, turn 'em all off, then turn one on roll : function(e, i) { YAHOO.util.Dom.removeClass(btns, 'btn-over'); YAHOO.util.Dom.addClass(btns[i], 'btn-over'); } }; // Initialize the menu YAHOO.util.Event.on(window, 'load', menu.init); </script>
The YUI version basically works just like the unobtrusive version. Yahoo! provides hosted versions of all the YUI extensions, just add a link to yahoo-dom-event.js. With the help of the Yahoo! Global Object and DOM and Event extensions, it's easy to grab button elements and assign event handlers that trigger the roll function. The main difference here is that everything is neatly encapsulated into the menu object. The YUI's getElementsByClassName and remove/addClass methods make DOM scripting a breeze.
Take a look at the final product.
Hopefully these basic examples serve as a kick in the rear for those clinging to those old school ways. Now that you have the basics of event handling down, I'll show you how to improve the scripts performance through event delegation and how to make it scalable to power multiple rollover menus.
| Attachment | Size |
|---|---|
| yui-rollover.css | 541 bytes |
| yui-rollover.html | 1.48 KB |
Comments
Thank you very much for
Thank you very much for this.bedava sohbet
One more tweak
use YUI's own Method to find the Target of the Event:
var target = YAHOO.util.Event.getTarget(eventObject);
Updating YUI example
Thanks for that Dirk. I'll rework the YUI example to incorporate this and other comments.
Multiple Event Attachments...
One comment is that you're adding a memory overhead for each event listener you're attaching. For a half-dozen rollovers, this isn't a problem, but you could potentially be attaching several dozen of them.
In such a case, you'd want to attach one event listener on a parent element and check for individual cases by determining the target of the button click. So, for instance, something like the following:
var over = function(eventObject) { // get around IE's quirky DOM handling var target = (eventObject.target) ? eventObject.target : eventObject.srcElement if (YAHOO.util.Dom.hasClass(target, 'btn')) { // it means we have a menu button YAHOO.util.Dom.addClass(target, 'btn-over'); } } YAHOO.util.Event.on('menu', 'mouseover', over);Companion post inspiration
Thanks Nick and Dirk. You provided inspiration for a companion post, "YUI Rollover Part 2: Improving JavaScript Performance and Scalability.
Thanks!
That's an incredibly helpful performance tip.
Post new comment