jQuery Events: MouseOver / MouseOut vs. MouseEnter / MouseLeave
Posted January 8, 2010 at 8:45 AM
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
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.
As always, good info Ben! I've always been curious about the differences in those events as initially they sound so similar.
@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.
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.
Ben, What is this source reference: src="jquery-1.4a2.js" ?
What version of jQuery is that?
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');
});
@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.
Thank you for the article. It's well made and very heplfull:)
@Luke,
Glad you got some value out of it.
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?
@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.
@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.
@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.
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.
@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.
@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.
@Amy,
Will do, thank you kindly for the offer.
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?
@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?
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.
@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?
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.
Oh, weird, now the first 2 posts changed back to my name again.
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!
@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.
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?
@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()



