I want to trigger an event when my mouse enters a div, and a separate event when it leaves the div using jQuery. I know there are plenty of plugins, code samples, etc. that solve this particular problem. I'm just curious which way would be most efficient in terms of both performance and memory management.
So for instance, assume we have HTML code like this:
<div class="mouse-trap"> 1 </div>
<div class="mouse-trap"> 2 </div>
...
<div class="mouse-trap"> n-1 </div>
<div class="mouse-trap"> n </div>
1. Separate events
We can maintain the events separately. This code is readable, and easy to work with.
$(".mouse-trap").bind('mouseenter', function() {
// mouse came in
});
$(".mouse-trap").bind('mouseleave', function() {
// mouse left
});
2. Nested events
Instead of always keeping a "mouseleave" event around, only create it when it's necessary and then trash it when we're done, since there will only ever be one mouseleave event for each mouseenter event.
$(".mouse-trap").bind('mouseenter', function() {
// mouse came in
$(this).one('mouseleave', function() {
// mouse left
});
});
In the first scenario if you have n events, this means jQuery is constantly have to listen for n x 2 events.
In the second scenario, assuming the div's aren't nested inside each other, you're only listening for n + 1 events at any given point.
Admittedly I'm not sure exactly how much time is involved with event lookups, but I'd suspect from browser to browser, and computer to computer it would be different. For a relatively small value of n this would probably never be an issue. But what if n was really large? Would there be a noticeable difference?
Now, how do things change if you can have divs nested inside of each other?
For example:
<div class="mouse-trap">
<div class="mouse-trap">
<div class="mouse-trap">
...
</div>
<div class="mouse-trap">
...
</div>
</div>
...
<div class="mouse-trap">
...
</div>
</div>
The worst case scenario here would be n divs nested inside each other.
Because they are nested, I believe the "mouseleave" event won't get called on the outer divs as you move inwards. In this case I believe both scenarios would have to keep track of n x 2 events, but only when mousing over the inner-most div.
Now this brings up another question of how much overhead is required to bind and unbind events. I thought I read a while back that jQuery keeps all events in a queue, although I wasn't able to find specific documentation confirming that when researching this issue. So depending on the data structure used, the queuing/dequeuing probably has either a O(1) or O(n) cost. Does anybody know the specifics of this?
In scenario 1, you bind everything once up front and you don't have to mess with it anymore (barring new div.mouse-traps getting dynamically added to the page). That's n x 2 bindings that take place.
In scenario 2, you only bind n events up front, but there could theoretically be no limit to how many times you enter and leave an area with your mouse. These creates a possibility for infinite bindings necessary. But in the same respect, a mouse (and the human that operates that mouse) can only move so quickly, so these bindings are also all spread out over a greater period of time. This could mini开发者_如何学Gomize the effect of the numerous bindings.
All of that to say, I think the second scenario is probably better, but am I overlooking something else? Are there other ways of doing it that are better than the two that I listed?
I apologize for the lengthiness of this post. I just wanted to make sure I elaborated on my current thought process. I'm working on something that will have a large number of mouse events and want to make sure I play nicely with people's systems.
Wrap all .mouse-trap
in div
, bind the events only once on that and use event.target
to handle it.
As a side note - you should profile it and see which solution it the best for you.
may be better like this:
jQuery.fn.mouse_trap=function(f1,f2)
{
var obj=$(this);
obj.one('mouseenter',function(){enter(obj)});
function enter(obj) {
f1(obj);
obj.one('mouseleave',function(){leave(obj)})
}
function leave(obj) {
f2(obj);
obj.one('mouseenter',function(){enter(obj)})
}}
$(".mouse-trap").mouse_trap(function() {
// mouse came in
},function() {
// mouse came out
});
so jQuery will listen only n events may be we can create plugin for N where N is number of nested trees, but it will need to keep data of all elements somewhere which will make events themself slower
Even though I don't really have all the knowledge about the toll those two solutions would take on the performance of the project, I would personally lean towards the first avenue, separate events.
And about some of your concerns :
As Timothy Jones said in a comment, you have to consider the scope included in the closures you're binding can have a great memory impact. As the article states, using the arguments of a function inside a callback can potentially cause performance problems (which I didn't know until now, good to know). Even though you are not exactly doing so in your approach, the way you attach your events is not very far from it and you could potentially fall into the trap as your development progresses. Having your two separate events, you won't even have to worry about that.
In the nested elements scenario, as I suggested in my comment, you could go for a
mouseover
andmouseout
approach instead. It should take care of the 'entering the child while still being in the parent' as entering a child withmouseover
will trigger amouseout
on the parent.Lastly, about the number of active listeners binded, as eicto suggested, unbinding the current and then binding the opposed event as they are called should, in theory, cut the number of total attached events in half.
As I said, this is no technical answer, but for having developed something similar, I found this avenue (separate events) to be best suited both for flexibility and clarity in the end.
How about O(2) solution like this:
HTML:
<div class="mouse-trap-container">
<div class="mouse-trap"> 1 </div>
<div class="mouse-trap"> 2 </div>
...
<div class="mouse-trap"> n-1 </div>
<div class="mouse-trap"> n </div>
</div>
JS:
$('div.mouse-trap-container')
.delegate('div.mouse-trap', 'mouseenter', function(e) {
// mouse came in
}).delegate('div.mouse-trap', 'mouseleave', function(e) {
// mouse left
});
Perhaps this isn't the answer you're looking for, but..
Don't worry so much about performance/memory management - for today's computers this particular issue is such a trivial problem that it's not even worth analyzing at such a granular level. I think much more important is the cleanliness and maintainability of your code. So keep it simple with a hover() function:
$('.mouse-trap').hover(
function() {
// mouse in
},
function() {
// mouse out
}
);
精彩评论