Animated navigation items using jQuery

Dave Shea recently published an article on A List Apart (ALA), CSS Sprites2 – It’s JavaScript Time‘, about how to use jQuery to create the effect of animated rollovers on navigation items.

The technique he outlines makes use of the same image replacement method as outlined in ALA’s original Sprites article. The problem with this method however is that it uses a large negative text-indent to remove the default text from screen, and with images turned off in the browser, you don’t see anything. This has accessibility implications not only from the perspective of those with disabilities, but also for those who deliberately turn images off, i.e. people on slower connections or those using handheld devices who are trying to limit the amount of information downloaded to their phone.

When I do use image replacement, I prefer a method which leaves the text on screen when images are turned off – the Gilder Levin Ryznar Jacoubsen IR method, which I’ve written about before. Having been using jQuery quite a bit myself lately, I thought I’d see if I could come up with a similar implementation which would let me use the image replacement method I prefer.

Here’s my working example.

The mark-up is essentially the same as that Dave has used:

<ul id="nav">
    <li id="n-home"><a href="#home"><em></em>Home</a></li>
    <li id="n-about"><a href="#about"><em></em>About</a></li>
    <li id="n-services"><a href="#services"><em></em>Services</a></li>
    <li id="n-contact"><a href="#contact"><em></em>Contact</a></li>
</ul>

except that I’m using IDs on my list items and rather than having a class on the <ul> to identify the current page, I’ve got an ID on the body. The CSS looks like this:

#nav {
      width: 401px;
      height: 48px;
      margin: 0;
      padding: 0;
      background: url(images/animated-nav.png) repeat-x;
      list-style: none;
      overflow: hidden
  }

  #nav li {
      position: absolute;
      overflow: hidden;
      font-size: 1em;
  }

  #nav li, #nav li * { height: 48px }
  #nav a { display: block }
  #nav em, #nav span {
      display: block;
      position: absolute;
      top: 0; left: 0;
      z-index: 1;
      background: url(images/animated-nav.png) no-repeat;
      cursor: pointer;
  }

  #nav span { display: none }

  #n-home { left: 23px }
  #n-home, #n-home * { width: 77px }
  #n-home em { background-position: -23px 0 }
  #n-home:hover em, #n-home span, #home #n-home em { background-position: -23px -49px }
  #n-about, #n-about * { width: 83px }
  #n-about { left: 99px }
  #n-about em { background-position: -99px 0 }
  #n-about:hover em, #n-about span, #about #n-about em { background-position: -99px -49px }
  #n-services, #n-services * { width: 98px }
  #n-services { left: 182px }
  #n-services em { background-position: -182px 0 }
  #n-services:hover em, #n-services span, #services #n-services em { background-position: -182px -49px }
  #n-contact, #n-contact * { width: 98px }
  #n-contact { left: 280px }
  #n-contact em { background-position: -280px 0 }
  #n-contact:hover em, #n-contact span, #contact #n-contact em { background-position: -280px -49px }

  #nav .over { text-indent: -999em }
  #nav .over em { background-image: none }

The only additional rules added to get the animated effect to work are:

#nav span { display: none }
#nav .over { text-indent: -999em }
#nav .over em { background-image: none }

And the javascript:

$(document).ready(function(){
    // Get the ID of the body
    var parentID = $("body").attr("id");
    // Loop through the nav list items
    $("#nav li").each(function() {
        // compare IDs of the body and list-items
        var myID = $(this).attr("id");
        // only perform the change on hover if the IDs don't match (so the active link doesn't change on hover)
        if (myID != "n-" + parentID) {
            // for mouse actions
            $(this).children("a").hover(function() {
                // add a class to the list item so that additional styling can be applied to the <em> and the text
                $(this).addClass('over');
                // add in the span that will be faded in and out
                $(this).append("<span><\/span>");
                $(this).find("span").fadeIn(400);
            }, function() {
                $(this).removeClass('over');
                // fade out the span then remove it completely to prevent the animations from continuing to run if you move over different items quickly
                $(this).find("span").fadeOut(400, function() {
                    $(this).remove();
                });
            });
            // for keyboard actions
            $(this).children("a").focus(function() {
                $(this).addClass('over');
                $(this).append("<span><\/span>");
                $(this).find("span").fadeIn(400);
            });
            $(this).children("a").blur(function() {
                $(this).removeClass('over');
                $(this).find("span").fadeOut(400, function() {
                    $(this).remove();
                });
            });
        }
    });
});

Essentially, what’s happening is that javascript is being used to append an empty <span> to each anchor which is set to display: none by the CSS on page load, and when the mouse hovers over it, the <span> is animated to fade in.

Except that it’s not quite that simple. Because there’s already an empty <em> in each anchor which handles the rollover effect if javascript isn’t available, and because the change of the background-image on hover happens immediately, you don’t really notice the fading in of the hover state of the image because it’s fading in over the top of an image of itself.

So what I needed to do was to remove the background-image from the <em> when each anchor was hovered over – #nav .over em { background-image: none }. But this would then leave the text of each link visible, so I also needed to use text-indent to remove it from view – #nav .over { text-indent: -999em }. Using a negative text-indent was one of the reasons I shied away from the technique used in the ALA article, but at least in this example, the text is still visible in its normal state if images are turned off and only disappears when hovered over.

The javascript for this example is a bit lighter than the ALA example and it ticks a couple more accessibility boxes in that the animated effect will work for both mouse and keyboard-only users with both :hover and :focus events, and the text of the links is still visible with images turned off, but as mentioned above, it does still need to use text-indent: -999em which I would’ve preferred not to have used. So if anyone has any ideas about how to remove the need for text-indent, I’d like to hear them.

Browse by tags:

Tags: , , , , , ,

Share this article:

  • del.icio.us
  • Digg
  • Ma.gnolia
  • Reddit
  • StumbleUpon
  • Design Float

Subscribe to this site for regular updates

15 responses to Animated navigation items using jQuery. Add your own.

Comments

  1. 1

    That is quite nice, the only thing stopping me from using it is if you mouse over the buttons quickly (move it left to right quickly multiple times and then away) the effect keeps running for a while. Can you work out a way to stop the animation from doing this?

  2. 2

    Hi Ryan, thanks for pointing that out.

    I’ve now updated the script so that it only adds the extra span when you :hover/:focus on a link and then removes it again when you move away.

    This prevents the animations from continuing to accumulate for each link you hover over and playing out after you’ve stopped moving over the links.

  3. 3

    That’s great thanks for updating it.

  4. 4

    John, that’s outstanding. Image replacement, keyboard focus… much better!

  5. 5

    Thanks Mike. :) If I could only find a way for the text not to be indented on hover/focus, I’d be happy with it.

  6. 6

    Hi, nice post

Pingbacks

  1. 1

    [...]jQuery ile menü elemanlarına efekt vermek.[...]

  2. 2

    [...] Animated Navigation Items Using jQuery - Good article John! [...]

  3. 3

    [...] Animated navigation items using jQuery — Tyssen Design [...]

  4. 4

    [...] Read the full article at tyssendesign.com.au [...]

  5. 5

    [...] Animated navigation items using jQuery — Tyssen Design [...]

  6. 6

    [...] the perspective of those with disabilities, but also for those who deliberately turn images off, i.e. people on slower connections or those using handheld devices who are trying to limit the amount of information downloaded to their phone. Source:Tyssen Design. [...]

  7. 7

    [...] Animated navigation items using jQuery [tyssendesign.com.au] [...]

  8. 8

    [...]その他のjQuiryとかCSSを使ったアニメーション効果色々。Animated navigation items using jQuery[tyssendesign.com.au][...]

  9. 9

    [...] Buttons zu animieren. Nico hat hier ebenfalls ein kleines Tutorial geschrieben. Viele andere bieten ähnliche Lösungen an. Mich erinnert es an meine allerersten Versuche im Web, als Flash noch für [...]

Feed for this post's comments


Required indicates required field.
Email will not be published

You can use these tags in your reply:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Leave a Reply

Contact details