Function timeout - javascript

I'm trying to write a code that adds a class to a div for a limited time, and then removes it.
I tried using javascript's setTimeout, and jQuery's delay, but nothing works.
The element is SET but never REMOVED.
Here's the come I came up with:
window.onload = function() {
$(".button").click(handler);
}
function handler() {
$(this).addClass("onclick");
setTimeout(function() { $(this).removeClass("onclick"); }, 3000); // JS's setTimeout
$(this).addClass("onclick").delay(3000).removeClass("onclick"); // jQuery's delay
}
I don't get what's wrong... I even tried writing a second handler for the setTimeout function.
Thanks in advanced.

The problem you're having is that this is different within the function you're passing to setTimeout than it is outside it. The usual fix is to use the closure by creating a variable to hold it, and using the variable instead:
function handler() {
var $elm = $(this);
$elm.addClass("onclick");
setTimeout(function() {
$elm.removeClass("onclick");
}, 3000);
}
There I've also use the var to cache the result of $(this) because there's no point in doing it more than once.
More background:
In JavaScript, unlike some languages that look similar, this is defined entirely by how a function is called. When you use setTimeout, the way the function gets called will make this be the global object (window, on browsers), so that's why $(this).removeClass(...) wasn't working.
More on this if you're interested:
Mythical methods
You must remember this

this inside the setTimeout call does not refer to the clicked element.
Change it to this:
function handler() {
var t = $(this);
t.addClass("onclick");
setTimeout(function() { t.removeClass("onclick"); }, 3000);
}
Working example - http://jsfiddle.net/5vakN/
Reference for how this works in javascript - http://bonsaiden.github.com/JavaScript-Garden/#function.this

Related

JQuery named functions with scope

I need to execute a single function on a set of adjacent DIVS on my page. I thought it would work best like this:
function namedFunction(){
setTimeout(3000);
//do animations and such
$(this).next().call(namedFunction);
}
$("#firstdiv").call(namedFunction());
but I can't find a way to arbitrarily execute a function in a set scope (the firstdiv object), and moreover I'm not even sure if the scope will be passed correctly. Is there a way to do this, or a better way to do this?
I guess you want this:
function namedFunction () {
this.animate({ ... }, function () {
namedFunction.call( $( this ).next() );
});
}
and then:
namedFunction.call( $('#firstdiv') );
The setTimeout() function doesn't pause the current execution, it queues a future execution of a function that you supply. To do something when the page loads use a document ready handler.
I think you want something like this:
$(document).ready(function() {
var $divs = $("div.commonClass"),
i = 0;
function processNextDiv() {
namedFunction($divs.eq(i));
i = (i + 1) % $divs.length;
setTimeout(processNextDiv, 3000);
}
processNextDiv();
});
This first selects all the divs (I'd suggest doing so by giving them a common class, but you can do that any way you like), then sets up a timeout based loop that calls namedFunction() passing it the current div to be processed. So then you could have:
function namedFunction($div) {
$div.animate(...); // process current div
}
Or you could put the animation or other div processing code directly in that processNextDiv() function, depending on whether you want to be able to call namedFunction() from other places.
Demo: http://jsfiddle.net/hWLTb/
Maybe this?
$("#firstdiv").nextAll("div").andSelf().each(function(){
// this = DIV
});

Delay 6 seconds before beginning a function that will loop itself, small, not working, why?

I'm trying to create a 6 second delay before the heartColor(e) function begins, the function will then continue to loop. I don't understand why it's starting the function immediatley, and not waiting the 6 seconds it's supposed to, what did I do wrong?
function heartColor(e) {
e.animate({
color: '#7ea0dd'
}, 1000).animate({
color: '#986db9'
}, 1000).animate({
color: '#9fc54e'
}, 1000, function(){
heartColor(e)
})
}
$('.something').hover(function(){
setTimeout(heartColor($(this)), 6000);
})
The setTimeout() function expects its first parameter to be a function reference (or a string, but that's not recommended for several reasons). You are not passing it a function reference, you are calling the heartColor() function and passing the result to setTimeout(). So the function executes immediately, and then after six seconds nothing happens because the return value was undefined so setTimeout() had nothing to work with.
Try this instead:
$('.something').hover(function(){
var $this = $(this);
setTimeout(function() {
heartColor($this);
}, 6000);
})
The reason I have included an anonymous function as the parameter to setTimeout is that your call to heartColor() needs to pass a parameter through. If it didn't have any parameters you could do this:
setTimeout(heartColor, 6000);
Note there are no parentheses after heartColor - that gets a reference to the function without calling it so that later setTimeout calls it for you. But you can't get a reference to the function and provide parameters at the same time so you need to wrap the call up in another function. You could do this:
var $this = $(this);
function callHeartColor() {
heartColor($this);
}
setTimeout(callHeartColor, 6000);
My original answer with the anonymous function is kind of short hand for that and (most people find it) more convenient.
The reason I have created a variable $this is because of the way the this keyword works in JavaScript, which depends on how a function is called. If you simply said heartColor($(this)) inside the anonymous function (or the callHeartColor() function) this would not be the element being hovered over.
you are invoking the function heartColor instead of passing it as a parameter. you have to do:
$('.something').hover(function(){
setTimeout(function(){heartColor($(this))}, 6000);
})
You want this:
$('.something').hover(function(){
setTimeout(function() {heartColor($(this));}, 6000);
})

setTimeout issue in Firefox

The menu system is supposed to expand and collapse according to a given delay using the following statements (o_item.getprop('hide_delay') returns 200 and o_item.getprop('expd_delay') returns 0):
this.o_showtimer = setTimeout('A_MENUS['+ this.n_id +'].expand(' + n_id + ');',
o_item.getprop('expd_delay'));
and
this.o_hidetimer = setTimeout('A_MENUS['+ this.n_id +'].collapse();',
o_item.getprop('hide_delay'));
I tried placing the code for the first argument into separate functions and call these functions as the first argument to setTimeout like this:
this.o_showtimer = setTimeout( expandItem(this.n_id, n_id),
o_item.getprop('expd_delay'));
Firebug produced the following error message:
useless setTimeout call (missing quotes around argument?)
And there was no delay in the collapse.
I placed the argument in quotes (though recommended against here) like this:
this.o_showtimer = setTimeout( "expandItem(this.n_id, n_id)",
o_item.getprop('expd_delay'));
but this didn't work. It appeared that nothing was happening at all and throwing some console.log() messages into the code confirmed this.
I tried using an anonymous function call as recommended here and here like this:
this.o_showtimer = setTimeout( function() { expandItem(this.n_id, n_id); },
o_item.getprop('expd_delay'));
but this didn't work either. It produced undesirable results in IE (items not collapsing is the same manner as before) and nothing happening in Firefox (placing console.log() statements in expandItem and collapseItem functions confirmed that they weren't being called).
I even tried doing the following:
this.o_hidetimer = setTimeout( function() { alert('test'); },
o_item.getprop('hide_delay'));
and that didn't even work! Seems there's something up with calling the anonymous function.
Discovered that assigning the value of setTimeout to a variable other than this.o_showtimer made the left argument of setTimeout fire. Must be something to do with assigning something to this.
If I do this:
var o_showtimer = setTimeout( function() { expandItem(this.n_id, n_id); },
o_item.getprop('expd_delay'));
expandItem gets called. However, if I do this:
var o_showtimer = setTimeout( function() { expandItem(this.n_id, n_id); },
o_item.getprop('expd_delay'));
this.o_showtimer = o_showtimer;
As if setTimeout can predict the future! (expd_delay is 0!).
I think the problem is in Javascript's idiosyncratic treatment of 'this'. When you call 'expandItem' within your anonymous function, you are not calling it as a method, so 'this' gets set to the fundamental scope (window).
I would suggest using a local variable
var that = this;
this.o_showtimer = setTimeout( function() { expandItem(that.n_id, n_id); },
o_item.getprop('expd_delay'));

window.setInterval from inside an object

I'm currently having an issue where I have a javascript object that is trying to use setInterval to call a private function inside of itself. However, it can't find the object when I try to call it. I have a feeling that it's because window.setInterval is trying to call into the object from outside but doesn't have a reference to the object. FWIW - I can't get it to work with the function being public either.
The basic requirement is that I may need to have multiple instances of this object to track multiple uploads that are occurring at once. If you have a better design than the current one or can get the current one working then I'm all ears.
The following code is meant to continuously ping a web service to get the status of my file upload:
var FileUploader = function(uploadKey) {
var intervalId;
var UpdateProgress = function() {
$.get('someWebService', {},
function(json) {
alert('success');
});
};
return {
BeginTrackProgress: function() {
intervalId = window.setInterval('UpdateProgress()', 1500);
},
EndTrackProgress: function() {
clearInterval(intervalId);
}
};
};
This is how it is being called:
var fileUploader = new FileUploader('myFileKey');
fileUploader.BeginTrackProgress();
Use this
intervalId = window.setInterval(UpdateProgress, 1500);
setInterval with a literal argument will eval this in the global scope where UpdateProgress is not accessible.
Because it is an eval expression, it does not have access to the scope that setInterval is created in. Try:
intervalId = window.setInterval(UpdateProgress, 1500)
It is generally good practice to avoid eval style expressions wherever possible. For instance, if you wanted to call several functions from the same timer, you would use an anonymous function instead of a string.
window.setInterval(function () {
function1();
function2();
}, 1500)
See also
Why is using javascript eval() a bad idea?
Anonymous function - Wikipedia
+1 to Andy E's head (I can't upvote yet, doh!)
Another gotcha that could get you is if you use this from within the called function.
Then doing exactly what Andy has with this addition should get you by.
var that = this;
window.setInterval(function() {
function1.apply(that);
function2.apply(that);
}, 1500);

Calling Functions in Objects with Javascript

I have an object defined like this:
Blah = {
hideTimer:null,
setTimer: function() {
this.hideTimer = window.setTimeout(Blah.hidePopupInner, 500);
// must be done via window due to Greasemonkey
},
hidePopupInner: function() {
log("This? " + this);
},
hidePopupInnerPublic: function() {
Blah.hidePopupInner();
}
}
The problem is that the 'this' in killTimer is not set to Blah. If I change the line to say
this.hideTimer = window.setTimeout(Blah.hidePopupInnerPublic, 500);
then the 'this' is pointing to Blah so the hideTimer can be utilized.
Making a 'public' method for each method solves the problem, but there must be an easier solution...?
Note: This is all in Greasemonkey, but I think it's a general Javascript question.
To solve this, you can use anonymous function and scope reference when building timeout.
(code...)
setTimer: function() {
var _this = this;
this.hideTimer = window.setTimeout(function(ms){
_this.hidePopupInner();
}, 500);
},
(code...)
PS: Moreover, setTimeout will pass the number of milliseconds to invoked function. For example: imagine your function can receive one parameter, and do some stuff with it. But because setTimeout will pass milliseconds to your function, it can lead to unexpected errors.
Basically function specified as setTimeout param is executed like callback.
Reason you're not getting Blah context is you switching to setTimeout scope (even when using Blah method).
I don't know Greasemonkey at all, however using Function methods like Bind will help you.
If there is no function like bind in GM, you can alwyas write it but yourself (couple of lines of code) - can copy PrototypeJS one.
http://www.prototypejs.org/api/function/bind
It basically executes prepares your function with specifed scope:
// inside Blah
setTimeout (Blah.hidePopupInner.bind(this), 500);
Actually Tableton's solution is Bind's implementation on fly
Though not a true solution to the scope issue, you can at least get around Blah.killTimerPublic by doing:
window.setTimeout(function(){ Blah.hidePopupInner() }, 500);

Categories