Quick Tip: Outline Elements on Hover

read 18 comments

Someone on the jQuery Google Group yesterday asked about how to display a border around elements on hover. Here is a quick script I wrote to achieve that effect:

JavaScript:
  1. $(document).ready(function() {
  2.   $('.entrytext')
  3.     .mouseover(function(event) {
  4.       $(event.target).addClass('outline-element');
  5.     })
  6.     .mouseout(function(event) {
  7.       $(event.target).removeClass('outline-element');
  8.     })
  9.     .click(function(event) {
  10.       $(event.target).toggleClass('outline-element-clicked');
  11.     });
  12. });

The script will respond to mouseover, mouseout, and click within <div class="entrytext">. I use mouseover/mouseout rather than mouseenter/mouseleave because the script relies on event bubbling. The $(event.target) selector ensures that the class manipulation occurs on the innermost element. You can try it out on this page by hovering over elements within the main content area.

The two classes that are being toggled have the following style rules:

CSS:
  1. .outlineElement {
  2.   outline: 1px solid #c00;
  3. }
  4.  
  5. .outlineElementClicked {
  6.   outline: 1px solid #0c0;
  7. }

The outline property is really handy for this sort of thing because it doesn't affect the element's dimensions or layout. Unfortunately, it isn't supported in Internet Explorer 6 or 7, so if you want to support them, you might want to use border or background-color instead in a separate IE-only stylesheet. Or, maybe IE has some proprietary property? Anyone know?

I'm not sure what the person wanted to do with this script. By itself, it doesn't seem particularly useful to me. Still, I think there are a few concepts in there that can be applied elsewhere, and maybe the script can be expanded to do something interesting.

Update

After thinking about this for a night, I realized that the script could be written more succinctly by combining the mouseover and mouseout event types in a single .bind() method and toggling the class therein …

JavaScript:
  1. $(document).ready(function() {
  2.   $('.entrytext')
  3.   .bind('mouseover mouseout', function(event) {
  4.     var $tgt = $(event.target);
  5.     if (!$tgt.closest('.syntax_hilite').length) {
  6.       $tgt.toggleClass('outline-element');
  7.     }
  8.   })
  9.   .click(function(event) {
  10.     $(event.target).toggleClass('outline-element-clicked');
  11.   });
  12. });

Notice that the updated script also takes advantage of the .closest() method, which was introduced in jQuery 1.3. I don't want to apply the outline to any of the syntax-highlighted code in the entry, so I make sure that neither the event target nor any of its ancestors has a class of "syntax_hilite." Remember, to test for the presence (or absence) of a selector, we need to include more than just the selector itself. Here I test for "not a length." What I'm actually looking for is a length of 0, but since 0 is "falsey" the ! operator works just fine here.

One More Tip

Come to think of it, I can make the script shorter still by including the click event type along with mouseover and mouseout. Then, inside the the handler, the class that is toggled is determined by event.type.

JavaScript:
  1. $(document).ready(function() {
  2.   $('.entrytext').bind('mouseover mouseout click', function(event) {
  3.     var $tgt = $(event.target);
  4.     if (!$tgt.closest('.syntax_hilite').length) {
  5.       $tgt.toggleClass(event.type == 'click' ? 'outline-element-clicked' : 'outline-element');      
  6.     }
  7.   });
  8. });

Sometimes when code becomes shorter, it can also get less readable and more difficult to modify later on. I'm not suggesting that the final code snippet is necessarily the best way to achieve the outcome. A lot depends on what else you're planning to do and how a certain piece fits into the project as a whole.

comment feed

18 comments

  1. Unfortunately, IE doesn't let you style the outline property.

    You should really use jQuery's $.hover() interaction helper instead of mouseover(), mouseout():

        $("#content").hover(
          function (e) {
            $(e.target).addClass('outline-element');
          },
          function (e) {
            $(e.target).removeClass('outline-element');
          }
        );
    
    • Hi Dylan,
      Yes, I mentioned IE's lack of support for the outline property in the post. That's why I suggest using border or background-color in an IE stylesheet.

      Also, jQuery's .hover() method won't work for this. It uses "mouseenter" and "mouseleave" internally:

      hover: function( fnOver, fnOut ) {
          return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
      }

      Try it yourself and see.

  2. Mario

    Maybe he wanted to do what you just said, outline without changing dimensions, for fixing IE bug a possible solution would be to play with absolute positions, and other divs, interesting...

  3. Mario

    I beleive ie only recognizes outline for img elements

  4. Frédéric Hewitt

    I use a similar method with "*" selector for debugging purpose under browser who don't have firebug or equivalent. Completed with an other bit of jQuery who put useful title attribute on every objects, it's a quick and good tools.

    $('*').each(function () {
        var title = this.tagName;
        if (this.id) {
            title += "#" + this.id;
        }
        if ( $(this).attr('class') ) {
            title += '.' + $(this).attr('class').split(' ').join('.');
        }
        $(this).attr('title', title);
    });

    So on hover you get the outine or border with the code of this article, and a title on the form "div#id.class1.class2".

    Or "how to find what is this little square who appear without reason in the corner under IE6..."

  5. One thing that was not mentioned. If you use IE, you'll need to use a transparent border and then colored border on hover, that way it doesn't shift your layout around 2 pixels.

  6. thursday0384

    Hi, I have a very basic jquery question that i've been trying to figure out for a while.

    I'm doing a logo display for my company site and I want the caption to
    fade in each time you hover over the logo and to fade out when you
    hover away. I've got that part squared away, but I wanted to know how
    to edit my code so that i can reuse my classes for each logo on the
    page. Right now if i hover over a logo, the text for each one fades in
    and fades out at the same time. I'm too new to scripting to know quite
    how to do it. I was thinking maybe using jquery's .each() method or a
    for each loop, but i don't know ... can anyone help put me on the
    right path?!

    here's the jquery

    
    $(document).ready(function(){
    
    $(".logo").hover(function(){
            $(".caption").animate({opacity:"show"}, "fast");
            },
            function(){
            $(".caption").animate({opacity:"hide"}, "slow");
        });
    
    });
    

    .logo is the over all box, and it includes .caption, which fades in

    html:

    
        Liquid Video
    
        We did the LVT logo.
        
    
    
    


    so how do i only act on one logo at a time without having to
    individually number each logo on the page?

    Thanks!

    (if you see this reposted multipled times, it's b/c it was never
    showing up after i initially posted it and i kept trying)

    • I suppose it depends where in the DOM .caption is in relation to .logo. If the caption is contained within the log, you could do something like this:

      $(document).ready(function(){
      
      $(".logo").hover(function(){
              $(this).find(".caption").animate({opacity:"show"}, "fast");
              },
              function(){
              $(this).find(".caption").animate({opacity:"hide"}, "slow");
          });
      
      });
      
  7. thursday0384

    sorry. something happened on the html part. disregard it

  8. Good job, I will translate your lessons to russian languages in jquerys.ru

  9. Jay

    You are right regarding the tradeoff between conciseness and clarity. I usually argue that clarity should win. In my opinion, the last example goes just a little too far. It is better to take advantage of the event system, let it do the work it should do.

    Real nice example in the post. Thanks to the author of rcontributing to the community.

  10. Nice post. Is there any way to include styles on the fly, like jQuerify?

  11. MySchizoBuddy

    I have a use for it specially inside a textarea. For eg when entering ASCIIMathML like so
    'y=x^2' I would like when the mouse cursor or keyboard cursor is inside the backticks(') then it should highlight the complete formula and right below it open up a preview of what exactly will y=x^2 be rendered as. Very very handly to type long technical articles with complex equations.

    Perhaps you can write a tutorial on that functionality. Use a dictionary as an example. when you hover over complex words it highlights it and show its meaning in a small preview underneath. like this image http://www.lifehacker.com/assets/resources/2006/09/os%20x%20dictionary.png

  12. Simon

    Thank you, thank you, thank you..

  13. Anthony Hurst

    This is great for debugging CSS issues.

  14. Aaron

    Simply amazing.

    Thanks for the article.

    Pretty neat :


    1.
    $(document).ready(function() {
    2.
    $('.entrytext')
    3.
    .bind('mouseover mouseout', function(event) {
    4.
    var $tgt = $(event.target);
    5.
    if (!$tgt.closest('.syntax_hilite').length) {
    6.
    $tgt.toggleClass('outline-element');
    7.
    }
    8.
    })
    9.
    .click(function(event) {
    10.
    $(event.target).toggleClass('outline-element-clicked');
    11.
    });
    12.
    });

  15. I love seeing the progression, how you wrote that first piece of code, slept on it, trimmed it down. Gave it some time, trimmed it down some more. As you said it's not that the final code you produced is the "best" (depends on the application), but seeing the progression of how you were able to keep fine tuning the code is awesome.

  16. Thanks for such excellent post. Really informative blog. Keep the good work here and i wish you all the best and success.

Leave a Comment

IMPORTANT:

  • If you wish to post code examples, please wrap them in <code> tags.
  • Multi-line code should be wrapped in <pre><code> </code></pre>
  • Use &lt; instead of < and &gt; instead of > in the examples themselves. Otherwise, you could lose part of the comment when it's submitted.