Quick Tip: Outline Elements on Hover

read 24 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:

Continue Reading Below
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

24 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");
          });
      
      });
      
      • Randy

        A little late here, but also if each associated caption was within the .logo classed element, as you are suggesting, then instead of using $.find() a second argument could be passed to the jQuery core for context:

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

        If captions were located away from the .logo classed elements within the DOM, then another method could be

        $('.caption').get($('.logo').index(this))

        replacing

        $('.caption',this)

        Each caption is then mapped to it's corresponding logo by it's indexed position within the DOM.

        If there is nothing relational between the DOM positioning of logo and caption (one is throwing them randomly into the DOM anywhere), then I would investigate any type of jQuery mapping methods or methods allowing data attachment to elements. This is beyond my studies of jQuery so far, but anything that could link the logo to it's caption could be implemented. Including slicing of id attribute values, which the original poster was wanting to avoid.

  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.

  17. Thomas

    What you did here can better be done with CSS and this might even work for those who diabled JS.

  18. Fre

    I don't really see the necessity of JQuery for the most part this example? This could just as easily be accomplished using only CSS and the :hover selector (which doesn't work in IE6, but since outline doesn't work either it wouldn't really matter).

    .entrytext *:hover {
           outline: 1px solid #c00;
    }

    The clicked outline would still have to be added with JQuery of course.

    And this could be useful in an online code editor, where you want to show the user where an element starts/ends to find bugs more easily. It's basically the same way CSSEdit2 on Mac works.

    • Amos

      The click event in JQ could be replaced with :active selector in CSS, so you dont need JS at all. I don't see the need of JS or JQ unless you want to add some fancy effects..

  19. Lacey

    Hi, Would it be possible to use what you've done here and then also let the user change the colour of the element they select from a widget? kinda like what firebug does but more permanent. Ive been trying to find a example but no luck can you help please :(

    thankyou

  20. Tomek

    Hi there,
    Great tip - simple, but do the trick perfectly. Thank you.
    One thing though - you're adding 'outline-element' class in your JS file when in CSS you've got 'outlineElement'.

    Cheers!

2 Pings

  1. [...] Quick Tip: Outline Elements on Hover (tags: tutorial javascript jquery css) February 16th, 2010 | Posted in Reading « links for 2010-01-28 [...]

  2. links for 2010-08-31 « andrewskinner.name

    [...] Quick Tip: Outline Elements on Hover » Learning jQuery – Tips, Techniques, Tutorials (tags: hover outline jquery howto dom) [...]

Sorry, but comments for this entry are now closed.