jQuery Events: MouseOver / MouseOut vs. MouseEnter / MouseLeave

Posted January 8, 2010 at 8:45 AM

Tags: Javascript / DHTML

For years, Javascript developers have had the MouseOver and MouseOut events for triggering events when a user mouses over and out of a given HTML element. For anyone who's tried to use these, you know that they are great, but that they come with a huge problem: the mouseover and mouseout events fire for a given element if the user mouses over or out of an element nested within the given element. To get around this behavior, we've had to resort to all short of shenanigans, including the use of Javascript timers to delay "out"-based actions (assuming an "over" action might subsequently cancel a timer).

 
 
 
 
 
 
 
 
 
 

jQuery removes this headache by introducing the custom events, MouseEnter and MouseLeave. These custom events build on top of the existing mouseover and mouseout events; they travel up the DOM with each mouseover / mouseout event triggering to see if the user has truly "entered" or "left" the given element. Now these events have been in jQuery for some time; but, I've really start to leverage them a lot lately and, let me say, they make life super easier!

  • <!DOCTYPE HTML>
  • <html>
  • <head>
  • <title>MouseOver / MouseOut vs. MouseEnter / MouseLeave</title>
  • <style type="text/css">
  •  
  • #outer-mouseover {
  • background-color: #F0F0F0 ;
  • border: 1px solid #D0D0D0 ;
  • float: left ;
  • height: 225px ;
  • margin-right: 20px ;
  • position: relative ;
  • width: 225px ;
  • }
  •  
  • #outer-mouseenter {
  • background-color: #F0F0F0 ;
  • border: 1px solid #D0D0D0 ;
  • float: left ;
  • height: 225px ;
  • position: relative ;
  • width: 225px ;
  • }
  •  
  • span.inner {
  • background-color: #B3C9DF ;
  • border: 1px solid #6492BF ;
  • color: #FFFFFF ;
  • height: 100px ;
  • left: 62px ;
  • line-height: 98px ;
  • position: absolute ;
  • text-align: center ;
  • top: 62px ;
  • width: 100px ;
  • }
  •  
  • </style>
  • <script type="text/javascript" src="jquery-1.4a2.js"></script>
  • <script type="text/javascript">
  •  
  • // When the DOM is ready, init scripts.
  • jQuery(function( $ ){
  •  
  • // Bind the mouse over /out to the first DIV.
  • $( "#outer-mouseover" ).bind(
  • "mouseover mouseout",
  • function( event ){
  • console.log( event.type, " :: ", this.id );
  • }
  • );
  •  
  • // Bind the mouse enter to the second DIV.
  • $( "#outer-mouseenter" ).bind(
  • "mouseenter mouseleave",
  • function( event ){
  • console.log( event.type, " :: ", this.id );
  • }
  • );
  •  
  • });
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • MouseOver / MouseOut vs. MouseEnter / MouseLeave
  • </h1>
  •  
  • <div id="outer-mouseover">
  • <span class="inner">MouseOver</span>
  • </div>
  •  
  • <div id="outer-mouseenter">
  • <span class="inner">MouseEnter</span>
  • </div>
  •  
  • </body>
  • </html>

If you watch the video above, you'll see that the mouseenter and mouseleave events stay more in alignment with the "intent" of the code, rather than the technical underpinnings.

jQuery takes these custom events and makes them even easier to use by wrapping them up in the convenience method, hover(). The hover() method takes two Function arguments and binds the first one as your mouseenter event handler and the second one as your mouseleave event handler:

  • <!DOCTYPE HTML>
  • <html>
  • <head>
  • <title>jQuery Hover Method</title>
  • <style type="text/css">
  •  
  • #outer-hover {
  • background-color: #F0F0F0 ;
  • border: 1px solid #D0D0D0 ;
  • height: 225px ;
  • margin-right: 20px ;
  • position: relative ;
  • width: 225px ;
  • }
  •  
  • span.inner {
  • background-color: #B3C9DF ;
  • border: 1px solid #6492BF ;
  • color: #FFFFFF ;
  • height: 100px ;
  • left: 62px ;
  • line-height: 98px ;
  • position: absolute ;
  • text-align: center ;
  • top: 62px ;
  • width: 100px ;
  • }
  •  
  • </style>
  • <script type="text/javascript" src="jquery-1.4a2.js"></script>
  • <script type="text/javascript">
  •  
  • // When the DOM is ready, init scripts.
  • jQuery(function( $ ){
  •  
  • // Bind the mouse enter and mouse leave events using
  • // the convenience method, hover().
  • $( "#outer-hover" ).hover(
  • function(){
  • console.log( "mouseEnter" );
  • },
  • function(){
  • console.log( "mouseLeave" );
  • }
  • );
  •  
  • });
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • jQuery Hover Method
  • </h1>
  •  
  • <div id="outer-hover">
  • <span class="inner">Hover</span>
  • </div>
  •  
  • </body>
  • </html>

As you can see, jQuery's hover() method simply wraps around the mouseenter() and mouseleave() event bindings.

Like I said above, these features have been part of jQuery for a while. But, I have recently started to take full advantage of them and they have just been amazing. It's definitely a small feature that makes a big impact on the way you code your interactions.



Reader Comments

Jan 8, 2010 at 9:40 AM // reply »
21 Comments

Ben,

Thanks for posting this...I am right in the middle of trying to solve this problem and this works awesomely.

The only difference is I am using ExtJS 3 and it works just the same. As an aside I'm finding a lot of similarities between ExtJS and jQuery.


Jan 8, 2010 at 9:56 AM // reply »
7 Comments

As always, good info Ben! I've always been curious about the differences in those events as initially they sound so similar.


Jan 8, 2010 at 5:32 PM // reply »
8,777 Comments

@Sam,

I've heard great things about ExtJS, especially when building real "applicationy" applications. But that aside, Hover() has been great for me recently.

@Ryan,

Heck yeah my man, glad to help clarify. For the longest time, I had no idea what the difference was. I think has become more useful recently, only because I finally get it.


Jan 11, 2010 at 10:25 AM // reply »
1 Comments

Nice quick demonstration.
I need exactly the same funcionality you demonstrated here but I should bind the events to future elements as well. Unfortunately jQuery's "live" currently (1.3.2) does not support mouseenter and mouseleave. Hope they will fix it in the next version.


Jan 11, 2010 at 10:34 AM // reply »
14 Comments

Ben, What is this source reference: src="jquery-1.4a2.js" ?

What version of jQuery is that?


Jan 11, 2010 at 10:52 AM // reply »
7 Comments

Just for clarification, in jQuery 1.4, the $.hover() event can take one function as an argument, as well as two. This is would allow you to pass in a single function that uses $.toggleX() or something similar.

It would be the technical equivalent of actually using the events (in a single bind) that you talk about:

$(...).bind('mouseenter mouseleave', function(){
$(this).toggleClass('yay');
});


Jan 11, 2010 at 2:06 PM // reply »
8,777 Comments

@Zoltan,

Hmm, good question. I can easily see something like this being used for dynamically added elements. It might be easier than we think; I'll put my thinking cap on.

@Kristopher,

It was an Alpha version of jQuery 1.4. Incidentally, version 1.4 was just released publicly.

@Alex,

Very interesting. I could see that being useful. When I was looking at the jQuery code, I did see that it did this:

.mouseout( fnOut || fnOver )

... but I had no idea why it was doing that. I guess that makes perfect sense in terms of toggling.


Jan 16, 2010 at 7:44 AM // reply »
1 Comments

Thank you for the article. It's well made and very heplfull:)


Jan 16, 2010 at 3:56 PM // reply »
8,777 Comments

@Luke,

Glad you got some value out of it.


Jan 18, 2010 at 12:06 PM // reply »
2 Comments

Thanks for posting this, it clarifies a lot!

I thought I'd extend this a bit further, but that does not seem to work. I have two div panels, and when hovering over the first, i am showing the second.
I hoped to do this using the hover() method:
$('#id1).add('#id2).hover(....

The code above triggers a leave and enter event, even when the two div (id1 and id2) are overlapping. Any ideas how to do this?


Jan 18, 2010 at 1:49 PM // reply »
8,777 Comments

@Pieter,

That's a very interesting situation - having two overlapping Divs with "hover" behavior. My guess is that you're gonna have trouble with that. I am not sure it's gonna be possible. You'd really have to look at the internals of the "mouseenter" / "mouseleave" events to see how they work, mechanically. If one prevents propagation of an event, I think you're gonna be out of luck.


Jan 18, 2010 at 2:25 PM // reply »
7 Comments

@Pieter,

We get that question a lot in the #jquery irc channel, and I think the code you wrote works exactly as it should, however not to your specifications.

Essentially what's happening: The hover functions that you pass into the event get applied to each matching element _individually_. So the fact that they overlap should have no bearing on their events being grouped together.

I built this example for someone on IRC a few months back and it's based on a popular technique called HoverIntent that uses timeouts to see if you move to a related object:

http://jsbin.com/ulili
http://jsbin.com/ulili/edit

The functionality I think you want should be happening when you stay hovered over either the button or the rollout div.


Jan 18, 2010 at 6:00 PM // reply »
2 Comments

@Alex and @Ben, thanks. It needs further testing, but it looks like it works now. Instead of using the mouseleave() event of one of the overlapping div's, I now use a mouseenter() event of the main page content to clean up the div panels.


Mar 4, 2010 at 8:30 AM // reply »
3 Comments

Thanks, Ben! :) This is another outstanding example of an easy to follow yet complete technical blog post. This article helped me a lot because I had been wasting a lot of time trying to create a tooltip with a dialog when I read your post, <slapped my forehead>, I should have just used the jQuery .hover event. I had all of my MVC view's hover dialog boxes tested and working in under an hour.


Mar 8, 2010 at 7:17 PM // reply »
8,777 Comments

@Amy,

Thanks a lot! You have no idea what kind of warm fuzzies it gives me to know that my writing is both useful and easy to understand. It is very appreciated.


Mar 9, 2010 at 6:25 AM // reply »
3 Comments

@Ben,

You are very welcome. :) I've been doing a LOT with ASP.NET MVC and jQuery lately, so please feel free to let me know if you have questions I can help you with too.


Mar 10, 2010 at 10:13 PM // reply »
8,777 Comments

@Amy,

Will do, thank you kindly for the offer.


Apr 19, 2010 at 5:56 AM // reply »
22 Comments

Any idea why this animates properly then - with no extra mouse movement - jumps back to the starting position a moment later? Does the same on mouseout to0.

$('#banner').hover(function(){
$(this).animate({backgroundPosition:"-250px 0"});
$(this).animate({backgroundPosition:"0 0"});
});

I've read of problems with hover firing off for no reason. I can't stop it.

Ideas?


Apr 19, 2010 at 9:22 PM // reply »
8,777 Comments

@Ziggy,

I am not sure I understand your issue. From your two animate calls, it looks like that is exactly what it is supposed to do - animate one way and then snap back. What are you intending it to do?


Apr 20, 2010 at 3:21 AM // reply »
22 Comments

No, it is snapping back without any further mouse movement, while still hovering - it should animate (not snap) back only when the mouse no longer hovers over the div.

Hover doesn't seem to work right, or not intuitively right anyway. Are you sure hover is using MouseEnter/MouseLeave? I think it must be using mouseover and mouseout.

I changed it to use .mouseenter ... .mouseleave and it works correctly now: mouseover and it animates one way and stays there until you mouseout then it animates back.

I also added px to the zeros, I think it fixed some jumping, forget now.

Thanks. Hope it helps.


Apr 21, 2010 at 9:34 AM // reply »
8,777 Comments

@Matt,

If you look under the hood, hover() just turns around and binds the mouseenter/mouseleave events. It's really just a convenient shorthand.

In your example above, it looks like you're using the shorter short-hand where you pass in one function and use it for BOTH the mouseenter and mouseleave events.

Is it possible that you made a typo passing in only a single function? Right now, you have *both* animate methods in a single event handler; did you mean to put on in the "enter" and one in the "leave" event handler?


Apr 27, 2010 at 3:18 AM // reply »
22 Comments

Ok, I see now it is supposed to be a comma in between not a semi-colon. Uhh, jquery and untold brackets and semi-colons and commas... Thanks!

Btw, my 2 comments above now say "Matt" made them. Something messed up there.


Apr 27, 2010 at 3:25 AM // reply »
22 Comments

Oh, weird, now the first 2 posts changed back to my name again.


May 24, 2010 at 12:16 AM // reply »
1 Comments

I'm new to using jQuery, but wanted to find out if the mouseEnter and mouseLeave functions could be used to replicate the "Get Social" icon behavior located in the top right of the following website: http://www.babyphat.com.

I was initially going to use Flash but have been reading that jQuery can perform some animations without being code intensive.

Any help or suggestions would be greatly appreciated.

Thanks!


Jun 9, 2010 at 9:58 PM // reply »
8,777 Comments

@Ziggy,

Yeah, there's some funky code behind the comment tracking because it integrates with "member" tracking on the points and comment count. It's a bit buggy from time to time.

@Kara,

There's not much that you can do in Flash that you can't do in jQuery. I don't have any specific suggestions other to say that the mouseEnter / mouseLeave can be used to create that kind of social stuff. After all, it's not much different than a drop-down menu except for that it's "dropping" left, not down.


Jul 14, 2010 at 6:58 AM // reply »
1 Comments

Hi, this works great except I made the function fadeIn and fadeOut a div using the .hover. But when I hover in and out repetitively the div starts to blink even when I am not hovering over it. Is there a way to set a timer or something to stop it from doing this?


Jul 18, 2010 at 12:04 PM // reply »
8,777 Comments

@Snowden,

This is probably happening because the events are getting queued as you enter/exit the target area. Try calling stop() before you execute the animation. This will stop any existing animation before starting the new one.

$( target ).stop().fadeOut()



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Formatting: <strong>bold</strong> <em>italic<em>







  • Help Wanted - Find Your Next ColdFusion Job
Recent Blog Comments
Sep 3, 2010 at 5:48 AM
Scope Behavior When Using CFThread Inside Of ColdFusion Components
Thanks Ben, Excellent article and very precise explanation. Cheers Philip A question on invoking asynchronous save or some task and returning response back to the calling page. Using cfThread is ... read »
Sep 3, 2010 at 3:04 AM
Long Polling Experiment With jQuery And ColdFusion
@Ben, Thank you for your answer. If you are interested in - I solved the problem. It was, as you said, a buffer issue. Now when I'm getting a new request, the first thing I do is I'm sending some fa ... read »
Sep 3, 2010 at 1:29 AM
Using jQuery's SlideUp() and SlideDown() Methods With Bottom-Positioned Elements
Hey Ben, Thanks for clearing this up! Also, is there a way for the container to be open when you first load the page, so that when u click on the link it will slideUp? ... read »
Sep 3, 2010 at 12:29 AM
Bidirectional Data Exchange With ColdFusion's CFThread Tag
Thanks for posting this example, Ben. I plan to put something like this to use. I want to spawn up a thread to insert several records (possibly 1000s) into a database incrementing a counter upon ea ... read »
Sep 2, 2010 at 11:23 PM
Experimenting With HTML5's Cache Manifest For Offline Web Applications
Hi Ben, having checked all articles on Html5's appCache, is there a solution to just update newer files, using the manifest file? I am looking into using application cache to actually have an offline ... read »
Sep 2, 2010 at 3:11 PM
Long Polling Experiment With jQuery And ColdFusion
@Alex, It looks like some of the browsers implement some sort of buffering on the data request. I was definitely finding different behavior across browsers. I want to come back and figure this code ... read »
Sep 2, 2010 at 3:09 PM
Creating Base64-Encoded Data URLs For Images In ColdFusion
@Randall, At the very least, I think Chrome won't be able to close windows unless it opens them up, right? I am not sure. ... read »
Sep 2, 2010 at 2:17 PM
ColdFusion NumberFormat() Exploration
Ben - I have same question as Jim and I think maybe you misread it? I want numbers with non-zero decimal places to display the decimal, but those that have no decimal to display w/o the decimal poin ... read »