Reputation: 3025
I have some jQuery code that looks like this:
$('.mainNav2 > li').mouseleave(function(){
var someNum = Math.random();
$(this).attr('id', someNum);
var t = setTimeout("HideMenu(someNum)", 200);
$('li.clicked').mouseenter(function() {
clearTimeout(t);
});
});
function HideMenu(id) {
$('#'+id).removeClass('clicked');
}
It's purpose is to hide a mega menu on mouse leave, but also takes into account accidental mouse leaves, by using a 300 millisecond setTimeout. If the user brings the mouse pointer back into the li within 300 milliseconds, the menu is not hidden because clearTimout(t) is called.
The problem is when the user DOES intent to mouseout, the function in the setTimout is not being called. According to this page: http://www.w3schools.com/js/js_timing.asp my syntax is correct, but I can only get the HideMenu function called from the setTimeout if I write it like this:
var t = setTimeout(HideMenu, 300);
Why isn't it working as written, where I can pass a variable into the function as a parameter?
Upvotes: 13
Views: 34753
Reputation: 185
This works, and can be used within a loop also.
var x = "OK";
setTimeout(alertOK.bind(null,x), 3000);
x = "Would be WRONG";
console.log("before timeout:", x);
function alertOK(x){
console.log("after timeout:",x);
}
Upvotes: 1
Reputation: 1647
While the marked correct answer is one method of achieving this... I don't believe it is the correct.
See the attached JS Fiddle: http://jsfiddle.net/PWHw3/
What we are doing here basically is:
setTimeout(function, timeout, param);
Example:
var test = function(a){
var b = (a) ? a : "fail";
alert(b);
};
setTimeout(test, 500, "works");
This works for me, and eliminates the needs to pass through two functions.
Upvotes: 19
Reputation: 26183
To make it work, and do it without using the nasty eval version of setTimeout change:
var t = setTimeout("HideMenu(someNum)", 200);
to this:
var t = setTimeout(function(s) {
return function() { HideMenu(s) } }(someNum), 200);
This way you pass the value of someNum
into the variable s
in the scope of the setTimeout
.
Upvotes: 12
Reputation: 281
setTimeout
accepts a function and a millisecond delay. The function can be either a function reference or a string that will get evaluated when the timeout fires. Your current sequence looks like this:
mouseleave function
someNum
a value. someNum is scoped to
the current function"HideNum(someNum)"
after 200ms.200ms passes
"HideNum(someNum)"
is evaluated. It should throw an exception as the variable someNum is undefined. That's why HideNum is not being called - check for errors in your console.What you want is a function reference, which will keep your someNum in scope (via closure - which you might want to read up on).
setTimeout(function() {
HideNum(someNum);
}, 200);
You'll find the Mozilla docs a better reference for JavaScript. Here's the window.setTimeout docs.
Upvotes: 3
Reputation:
There are more ways to do that.
1. Use an anonymous function (recommended)
setTimeout(function() {
hideMenu('someNum');
}, 200);
2. setTimeout
and setInterval
have a hidden feature: you can specify parameters after the timeout.
setTimeout(hideMenu, 200, params);
Since you're already using jQuery, you shouldn't make a separate function, but extend jQuery's prototype instead, like this:
jQuery.fn.hideMenu = function() {
this.removeClass('clicked');
return this;
};
Usage:
$('#num').hideMenu();
Notice that you don't need to pass this
to jQuery when you're extending its prototype, because it's already passed to jQuery. And return this;
is required to keep the ability of chaining.
If you choose this way, you need to use an anonymous functions, there's no easier way.
There's already a plugin for that: jQuery.hoverIntent()
. No need to make it yourself. It's easy to use, just replace mouseleave
event with this:
$('#someNum').hoverIntent(jQuery.noop, function() {
// your function goes here
});
It's important to do it this way, since the first one is the mouseenter
handler, and the second is the mouseleave
handler. jQuery.noop
is an empty function, it's effectively the same as function() {}
.
Upvotes: 1
Reputation: 385114
Because you're writing a string, which isn't as intelligent as an actual closure.
$('.mainNav2 > li').mouseleave(function(){
var someNum = Math.random();
$(this).attr('id', someNum);
var t = setTimeout(function() { HideMenu(someNum); }, 200);
$('li.clicked').mouseenter(function() {
clearTimeout(t);
});
});
function HideMenu(id) {
$('#'+id).removeClass('clicked');
}
Upvotes: 0
Reputation: 39223
What you want to do is to create an anonymous function and call that in the setTimeout call.
setTimeout(function() { HideMenu(someNum) }, 200);
If I correctly understand what you want to do, though, you shouldn't have to bother with setting an id and all that stuff. Something like this should do it:
$('.mainNav2 > li').mouseleave(function() {
var $this = $(this);
var t = setTimeout(function() {
$this.removeClass('clicked');
}, 200);
$('li.clicked').mouseenter(function() {
clearTimeout(t);
});
});
Upvotes: 2