The last few weeks I have actively been trying to learn javascript as opposed to just hacking and hoping. So today I decided to refactor a simple set of related functions in an existing app into a class just to tidy up the code and get a handle on creating classes in javascript.
Everything was going swimmingly until I came to the function that used "setTimeout()" .
The idea of this function is that it is called on a window resize and uses the setTimeout() function to wait for half a second to determine if the resizing has been finished before executing the desired event, in this case a function named hide.
var hideTimer;
function hide() {
// some code to hide an element
}
function waitHide() {
clearTimeout(hideTimer);
hideTimer = setTimeout(hide,500);
}
window.onresize = function(){
waitHide();
}
So I refactored the code and I ended up with this. Looks pretty simple right, and yet is doesn't work.
function hideTimer() {
this.timer;
}
hideTimer.prototype.hide() {
// some code to hide an element
}
hideTimer.prototype.waitHide() {
clearTimeout(this.timer);
this.timer = setTimeout(this.hide,500);
}
// Now create the timer instance
var testTimer = new hideTimer();
window.onresize = function(){
testTimer.waitHide();
}
When I tried to run this code I encountered was this error message in firebug.
The cause of this error turns out to be that the setInterval and setTimeout functions will take the scope to the window object. And as the hide function is wrapped inside the hideTimer object it is now effectively hidden from their view.
To fix this error we modify the setInterval/setTimeout function call using closures to pass the reference to the wrapped function.
hideTimer.prototype.waitHide() {
clearTimeout(this.timer);
this.timer = setTimeout(function(){this.hide();},500);
}
However this still does not fix the problem, we still get an error when we run the code. The second part of the problem is that we need to bind the hide() function call to this particular instance of the class.
To do this we create a self reference to this particular instance which we pass in in place of this. It sounds and looks redundant but it works.
hideTimer.prototype.waitHide() {
//Create a self reference to this instance
var self = this;
clearTimeout(this.timer);
//Call the setTimout() function but
//this time pass in self.hide() within the closure.
this.timer = setTimeout(function(){self.hide();},500);
}
There is a specification for newer browsers to use the bind method. I have not yet used this and I don't know what the browser support for it is like. However from what I understand this is how you would implement it.
hideTimer.prototype.waitHide() {
clearTimeout(this.timer);
this.timer = setTimeout( this.hide.bind(this), 500 );
}
I am only now really getting into really understanding javascript, but I still have to say that there are some very odd quirks to it.