I'm currently putting together a css animation, and part of achieving this involves changing the class name of the body at specific intervals.
Being quite new to mootools (and js in general) the best way I've thought of achieving this has been to simply add/remove classes to the body at delayed intervals, like so:
(function() {$(document.body).addClass('load');}).delay(20);
(function() {$(document.body).addClass('load-two');}).delay(2000);
(function() {$(document.body).addClass('load-three');}).delay(2700);
(function() {$(document.body).addClass('load-four');}).delay(4500);
However the more I read up on the subject, the more I'm convinced that this is an inefficient way of achieving what I want to.
The above code works in all the browsers I've tested it in, but would I be better using a chain class to achieve what I want? I have looked over the Mootools docs on setting up a chain, but for whatever reason, I'm struggling to get the demo working.
So the crux of what I'm asking, is if there's a better way of writing the code posted above, and what are the benefits of using a different method?
Thanks.
setting up a chain in mootools is quite simple. however, using the Chain class as a mixin can be a little more involved.
typically, it's geared up towards chaining of Fx-based classes and methods and not ones that are synchronous. say you have a tween effect which has link: chain in play, you can .chain(function() {}) the instance to do something else after.
the callChain example as a standalone is fine and easy enough but it offers little in terms of timing controls.
then there's the linear timeline approach. in your case, your first callback runs after 20 ms, 1980 ms after that the second, third runs 1680 ms after the second and so forth. if you chain things so that each successive step calls the next one, you need to take that into account and actually pass on the time to wait between the 2 actions instead.
the other way to do so is to just defer them as you have done from the start.
I had a go at streamlining the former a little here: http://jsfiddle.net/dimitar/mpzzq/
(function(){
Chain.implement({
slowChain: function(duration){
// console.log(duration);
this.callChain.delay(duration === null ? 500 : duration, this);
}
});
var db = $(document.body);
var fixBody = function(cn, delay) {
console.log(arguments);
db.addClass(cn);
console.log(cn, delay);
if (this.$chain.length) {
this.slowChain(delay || 0);
}
};
var myChain = new Chain(),
funcs = [{
fn: fixBody,
args: ["load"],
delay: 1980
}, {
fn: fixBody,
args: ["load-two"],
delay: 700
}, {
fn: fixBody,
args: ["load-three"],
delay: 2000
}, {
fn: fixBody,
args: ["load-four"],
delay: 0
}];
myChain.chain(
funcs.map(function(el) {
el.args.push(el.delay);
return el.fn.bind.apply(el.fn, [myChain].concat(el.args));
})
);
document.getElement("button").addEvents({
click: function() {
myChain.slowChain(20);
}
});
})();
so in my funcs array of objects i define the func callback, the arguments to pass on and the delay. keep in mind that the func itself has the this scope set to the chain instance and self calls next one on the chain but you can easily mod this and work with it.
hope it gives you some ideas.
here it is at take 2 with a decorator function that calls the chain on a delay:
// function decorator.
Function.implement({
chainDelay: function(delay, bind) {
// allows you to set a delay for chained funcs and auto call stack (if bind is a chain instance)
var self = this,
args = (arguments.length > 2) ? Array.slice(arguments, 2) : null;
return function() {
setTimeout(function() {
self.apply(bind, args.concat(Array.from(arguments)));
if (bind && bind.$chain && bind.$chain.length)
bind.callChain.call(bind);
}, delay);
}
},
justChain: function(bind) {
// runs a chained func when due and auto calls stack for next (if bind is a chain instance and avail)
var self = this, args = (arguments.length > 1) ? Array.slice(arguments, 1) : null;
return function() {
self.call(bind, args);
if (bind && bind.$chain && bind.$chain.length)
bind.callChain.call(bind);
}
}
});
var moo = new Chain();
moo.chain(
// some delayed ones.
(function(what) {
console.log(what);
}).chainDelay(3000, moo, "hi"),
(function(what, ever) {
console.log(what, ever);
}).chainDelay(3000, moo, "there", "coda"),
(function(what) {
new Element("div[id=foo][html=" + what +"]").inject(document.body);
}).chainDelay(1000, moo, "mootools FTW!"),
// regular ones here for good measure!
(function() {
document.id("foo").setStyle("color", "red")
}).justChain(moo),
(function() {
document.id("foo").setStyle("background-color", "blue")
})
);
moo.callChain();
example of that: http://jsfiddle.net/dimitar/Y4KCB/4/
Related
I'd like to find a way to detect whether a observer has finished using a customized observable, which I created with Rx.Observable.create, such that the customized observable can end it and do some clean up properly.
So I created some test code as below to figure out what kind of fields are available on the observer object for this purpose.
var Rx = require("rx")
var source = Rx.Observable.create(function (observer) {
var i = 0;
setInterval(function(){
observer.onNext(i);
console.dir(observer);
i+=1
}, 1000)
});
var subscription = source.take(2).subscribe(
function (x) { console.log('onNext: %s', x); }
);
The output is as following
onNext: 0
{ isStopped: false,
observer:
{ isStopped: false,
_onNext: [Function],
_onError: [Function],
_onCompleted: [Function] },
m: { isDisposed: false, current: { dispose: [Function] } } }
onNext: 1
onCompleted
{ isStopped: true,
observer:
{ isStopped: false,
_onNext: [Function],
_onError: [Function],
_onCompleted: [Function] },
m: { isDisposed: true, current: null } }
It seems there are 3 fields on the observer object which seem to have something to do withmy goal, namely, observer.isStopped, observer.observer.isStopped and observer.m.isDiposed.
I was wondering what they all are about and which one I should choose.
==============================================================================
Motivations for my question
Based on Andre's suggestion, I add the scenario which motivated my question.
In my application, I was trying to do some UI animations based on the window.requestAnimationFrame(callback) mechanism. requestAnimationFrame will call the provided callback in a time determined by the browser render engine. The callback is supposed to do some animation step and recursively call requestAnimationFrame again until the end of animation.
I want to abstract this mechanism to an observable as below.
function animationFrameRenderingEventsObservable(){
return Rx.Observable.create(function(subscriber){
var fn = function(frameTimestmpInMs){
subscriber.onNext(frameTimestmpInMs);
window.requestAnimationFrame(fn)
};
window.requestAnimationFrameb(fn);
});
}
Then I can use it in various places where animation are needed. For one example, I need to draw some animation UNTIL the user touch the screen, I go
animationFrameRenderingEventsObservable()
.takeUntil(touchStartEventObservable)
.subscribe( animationFunc )
However, I need a way to stop the infinite recursion in animationFrameRenderingEventsObservable after the takeUntil(touchStartEventObservable) has ended the subscription.
Therefore, I modified animationFrameRenderingEventsObservable to
function animationFrameRenderingEventsObservable(){
return Rx.Observable.create(function(subscriber){
var fn = function(frameTimestmpInMs){
if (!subscriber.isStopped){
subscriber.onNext(frameTimestmpInMs);
window.requestAnimationFrame(fn)
}else{
subscriber.onCompleted();
}
};
window.requestAnimationFrameb(fn);
});
}
According to my test, the code works as expected. But, if, as Andre mentioned, use subscriber.isStopped or alike is not a correct way to so, then what is the correct way?
In the function you supply to create, you can return a cleanup function to call when the observer unsubscribes from your observable. You should supply a function which stops your animation frame requests. Here's a working observable that does what you want that I wrote a few years ago:
Rx.Observable.animationFrames = function () {
/// <summary>
/// Returns an observable that triggers on every animation frame (see https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame ).
/// The value that comes through the observable is the time(ms) since the previous frame (or the time since the subscribe call for the first frame)
/// </summary>
var request = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame,
cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.webkitCancelRequestAnimationFrame ||
window.msCancelAnimationFrame || window.msCancelRequestAnimationFrame;
return Rx.Observable.create(function (observer) {
var requestId,
startTime = window.mozAnimationStartTime || Date.now(),
callback = function (currentTime) {
// If we have not been disposed, then request the next frame
if (requestId !== undefined) {
requestId = request(callback);
}
observer.onNext(Math.max(0, currentTime - startTime));
startTime = currentTime;
};
requestId = request(callback);
return function () {
if (requestId !== undefined) {
var r = requestId;
requestId = undefined;
cancel(r);
}
};
});
};
Usage:
Rx.Observable.animationFrames().take(5).subscribe(function (msSinceLastFrame) { ... });
If you using observer.isStopped or related, you doing something wrong. These are not API functions, they are implementation details.
I'd like to find a way to detect whether a observer has finished using a customized observable, which I created with Rx.Observable.create, such that the customized observable can end it and do some clean up properly.
Observers do clean up when 'onCompleted' happens. In the Observable.create above, you should call observer.onCompleted() when you consider the custom observable to have ended, or never call observer.onCompleted() if the custom observable is meant to be infinite. Also, you should wrap the whole code inside Observable.create with try/catch and call observer.onError(err) in the catch.
If you are trying to make the Observable "clean up" when an observer "has finished using the observable", then you are doing it wrong. Essentially, if the custom observable needs to react to the observer, then it means the observer should also be an observable. Likely, Observable.create is not the tool for this.
Better tell what you are trying to accomplish instead of how to do something specific.
UPDATE:
Based on the animations you want to do: in the context of RxJS, requestAnimationFrame is a Scheduler, not an Observable. Use it from the RxJS-DOM library.
Here is a simplified snippet from some code I wrote for managing tablet gestures on canvas elements
first a function that accepts an element and a dictionary of callbacks and register the events plus adding other features like 'hold' gestures:
function registerStageGestures(stage, callbacks, recieverArg) {
stage.inhold = false;
stage.timer = null;
var touchduration = 1000;
var reciever = recieverArg || window;
stage.onLongTouch = function(e) {
if (stage.timer) clearTimeout(stage.timer);
stage.inhold = true;
if (callbacks.touchholdstart) callbacks.touchholdstart.call(reciever, e);
};
stage.getContent().addEventListener('touchstart', function(e) {
e.preventDefault();
calcTouchEventData(e);
stage.timer = setTimeout(function() {
stage.onLongTouch(e);
}, touchduration);
if (callbacks.touchstart) callbacks.touchholdstart.call(reciever, e);
});
stage.getContent().addEventListener('touchmove', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdmove) callbacks.touchholdmove.call(reciever, e);
} else {
if (callbacks.touchmove) callbacks.touchmove.call(reciever, e);
}
});
stage.getContent().addEventListener('touchend', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdend) callbacks.touchholdend.call(reciever, e);
} else {
if (callbacks.touchend) callbacks.touchend.call(reciever, e);
}
stage.inhold = false;
});
}
later I call registerStageGestures on a few elements (represented by 'View' objects) in the same page. Something like:
function View() {
var self=this;
..
function InitView() {
...
registerStageGestures(kineticStage, {
touchstart: function(e) {
// do something
},
touchmove: function(e) {
// do something
},
touchendunction(e) {
// do something
},
touchholdstart: function(e) {
// do something
},
touchholdmove: function(e) {
// do something
},
touchholdend: function(e) {
// do something
},
}, self);
Everything works fine, however I'm left wondering about two things in the implementation of registerStageGestures:
First, is it necessary to make inhold, timer and onLongTouch members of the stage ? or will closures make everything works well if they are local vars in registerStageGestures ?
Second, is it necessary to call the callbacks with '.call(receiver,' syntax ? I'm doing this to make sure the callback code will run in the context of the View but I'm not sure if it's needed ?
any input is much appreciated
Thanks!
First, is it necessary to make inhold, timer and onLongTouch members
of the stage ? or will closures make everything works well if they are
local vars in registerStageGestures ?
As far as registerStageGestures() is concerned, var inhold, var timer and function onLongTouch(e) {...}. would suffice. The mechanism by which an inner function has automatic access to its outer function's members is known as "closure". You would only need to set stage.inhold, stage.timer and stage.onLongTouch if some other piece of code needs access to these settings as properties of stage.
Second, is it necessary to call the callbacks with '.call(receiver,'
syntax ? I'm doing this to make sure the callback code will run in the
context of the View but I'm not sure if it's needed ?
Possibly, depending on how those callbacks are written. .call() and .apply() are sometimes used when calling functions that use this internally. In both cases, the first parameter passed defines the object to be interpreted as this. Thus, javascript gives you the means of defining general purpose methods with no a priori assumption about the object to which those methods will apply when called. Similarly, you can call a method of an object in such a way that it acts on another object.
EDIT:
For completeness, please note that even in the absence of this in a function, .apply() can be very useful as it allows multiple parameters to be specified as elements of a single array, eg the ubiquitous jQuery.when.apply(null, arrayOfPromises)...
There are some simple answers, here.
First, closure:
Closure basically says that whatever is defined inside of a function, has access to the rest of that function's contents.
And all of those contents are guaranteed to stay alive (out of the trash), until there are no more objects left, which ere created inside.
A simple test:
var testClosure = function () {
var name = "Bob",
recallName = function () { return name; };
return { getName : recallName };
};
var test = testClosure();
console.log(test.getName()); // Bob
So anything that was created inside can be accessed by any function which was also created inside (or created inside of a function created in a function[, ...], inside).
var closure_2x = function () {
var name = "Bob",
innerScope = function () {
console.log(name);
return function () {
console.log("Still " + name);
}
};
return innerScope;
};
var inner_func = closure_2x();
var even_deeper = inner_func(); // "Bob"
even_deeper(); // "Still Bob"
This applies not only to variables/objects/functions created inside, but also to function arguments passed inside.
The arguments have no access to the inner-workings(unless passed to methods/callbacks), but the inner-workings will remember the arguments.
So as long as your functions are being created in the same scope as your values (or a child-scope), there's access.
.call is trickier.
You know what it does (replaces this inside of the function with the object you pass it)...
...but why and when, in this case are harder.
var Person = function (name, age) {
this.age = age;
this.getAge = function () {
return this.age;
};
};
var bob = new Person("Bob", 32);
This looks pretty normal.
Honestly, this could look a lot like Java or C# with a couple of tweaks.
bob.getAge(); // 32
Works like Java or C#, too.
doSomething.then(bob.getAge);
? Buh ?
We've now passed Bob's method into a function, as a function, all by itself.
var doug = { age : 28 };
doug.getAge = bob.getAge;
Now we've given doug a reference to directly use bobs methid -- not a copy, but a pointer to the actual method.
doug.getAge(); // 28
Well, that's odd.
What about what came out of passing it in as a callback?
var test = bob.getAge;
test(); // undefined
The reason for this, is, as you said, about context...
But the specific reason is because this inside of a function in JS isn't pre-compiled, or stored...
this is worked out on the fly, every time the function is called.
If you call
obj.method();
this === obj;
If you call
a.b.c.d();
this === a.b.c;
If you call
var test = bob.getAge;
test();
...?
this is equal to window.
In "strict mode" this doesn't happen (you get errors really quickly).
test.call(bob); //32
Balance restored!
Mostly...
There are still a few catches.
var outerScope = function () {
console.log(this.age);
var inner = function () {
console.log("Still " + this.age);
};
inner();
};
outerScope.call(bob);
// "32"
// "Still undefined"
This makes sense, when you think about it...
We know that if a function figures out this at the moment it's called -- scope has nothing to do with it...
...and we didn't add inner to an object...
this.inner = inner;
this.inner();
would have worked just fine (but now you just messed with an external object)...
So inner saw this as window.
The solution would either be to use .call, or .apply, or to use function-scoping and/or closure
var person = this,
inner = function () { console.log(person.age); };
The rabbit hole goes deeper, but my phone is dying...
I am trying implement async event leveraging YUI3 library. So the application had been notified about event passed even with late subscription, simular like load or ready events do.
Here it is what I have so far, but no luck around.
YUI().use('event', 'event-custom', function(Y){
function onCustomEvent () {
Y.Global.on('custom:event', function(){
alert('custom fired');
});
}
window.setTimeout(onCustomEvent, 2000);
});
YUI().use('event', 'event-custom', function(Y){
Y.publish('custom:event', {
emitFacade: true,
broadcast: 2,
fireOnce: true,
async: true
});
function fireCustomEvent () {
Y.Global.fire('custom:event');
}
window.setTimeout(fireCustomEvent, 1000);
});
If anyone could give a hint what's wrong with this code? Thank you.
UPD:
After a bit investigations it turns out that async events work fine inside one use() instance and when not using Global broadcasting. So that's something either bug or limitation. Still discovering
Okay, at the high level the inconsistency with global events (how I understood it) lays in the sandbox nature of Y object. So at some point you could fire only sync events globally cause async parameters you subscribe to custom event made on Y instance and not passed further (and than YUI uses some defaults or whatever). This possibly makes sense but than why such kind of events should be fireable globally? Either I miss some substantial part of YUI and this is candidate for bug report.
Anyway I do not have time to dive deeper in YUI and what I really practically need could be wrapped in 40 lines of code:
YUI.add('async-pubsub', function(Y) {
var subscribers = {};
if ( !YUI.asyncPubSub ) {
YUI.namespace('asyncPubSub');
YUI.asyncPubSub = (function(){
var eventsFired = {};
function doPublishFor(name) {
var subscriber;
for ( subscriber in subscribers ) {
if ( subscriber === name ) {
(subscribers[name]).call();
delete ( subscribers[name] ); // Keep Planet clean
}
}
}
return {
'publish': function(name, options) {
eventsFired[name] = options || {};
doPublishFor(name);
},
'subscribe': function(name, callback) {
if ( subscribers[name] ) {
Y.log('More than one async subscriber per instance, overriding it.', 'warning', 'async-pubsub');
}
subscribers[name] = callback || function() {};
if ( eventsFired[name] ) {
window.setTimeout(
function () {
doPublishFor(name);
},0
);
}
}
};
})();
}
Y.asyncPubSub = YUI.asyncPubSub;
}, '1.0', {requires: []});
There is some limitation and room for optimization here, like ability subscribe only one action for one event per use instance, but I do not need more. I will also try to debug and enhance this snippet in future if there will be interest.
Still curious about YUI behavior, is it bug or something?
My question is simple:
I'm passing a function to some other function to be call later (sample callback function), the question is when, why and what is the best practice to do it.
Sample:
I have the xxx() function, and I have to pass it, as I show you below in the window.onload event.
What is the best practice and why? There is any performance aspect or why should I choose to use call or bind to call this function
function xxx(text)
{
var div = document.createElement("div");
div.innerHTML = text + " - this: " + this.toString();
document.body.appendChild(div)
}
function callFunction(func)
{
func("callFunction");
}
function callUsingCall(func)
{
func.call(this, ["callUsingCall"]);
}
function callUsingBind(func)
{
func.call(this, ["callUsingCall"]);
}
window.onload = function(){
callFunction(xxx);
callUsingCall(xxx);
callUsingBind(xxx.bind(document));
}
Thank you,
Sebastian P.
I don't think there's any "best" practise.
You use call if the function you're calling cares what this is.
You use bind if you want to ensure that the function can only be called with the specified value of this.
[There's some overhead to both, i.e. at least one depth of function calls / scope]
Otherwise you just call the function.
Simples :)
The this object is the context of the function. It's like you make a machine that something for you, and the this object would be the place that the machine works in, like your house. You can move it as you like.
We have 4 ways setting this objects.
Calling the function that is not a method:
fn(someArguments)
This way the this object is set to null or probably the window object.
Calling the function as a method:
someObject.fn(someArguments)
In this case the this object will point to someObject and it's mutable.
Calling with call or apply methods of the function.
fn.call(anotherObject, someArguments)
someObject.call(anotherObject, someArguments)
someObject.apply(anotherObject, [someArguments])
In this case the this object will point to someObject here. You are forcing it to have another context, when calling it.
Binding a the function
var fn2 = fn.bind(anotherObject, someArguments)
This will create another function that is binded to that this object we gave it(anotherObject). No matter how you call it, the this object is going to be the same.
Use Cases
Now you can do some tricky stuff knowing this. The reason that why we have it here(I think it came first from C++) is that methods of an object need to access to their parent. The this object provides the access.
var coolObject = {
points : ['People are amazing'],
addPoint : function (p) { this.points.push(p) }
}
So if you do the following it won't work:
var addPoint = coolObject.addPoint;
addPoint('This will result in an error');
The error will be thrown because the this object is not our coolObject anymore and doesn't have the points property. So at times like this, you can something like this:
var addPoint = coolObject.addPoint;
addPoint.call({points : []}, 'This is pointless');
This is pointless, but the function will work, even the this object is not what its supposed to be.
var anotherCoolObject = {
points : ['Im a thief!'],
addPoint : coolObject.addPoint
}
anotherCoolObject.addPoint('THIS IS CALL STEALING');
Still the function will work if you call it like that, since the this object will point to anotherCoolObject which has the points property.
The most popular use case I've seen is slicing the arguments object:
function returnHalf() {
return [].slice.call(arguments, 0, arguments.length / 2);
}
returnHalf('Half', 'is', 'not', 'awesome');
// >> [Half', 'is']
So you see, arguments object is not an instanceof array. If we do arguments.slice(...) then you're gonna be killed by the compiler. But here we use the array's method on arguments object, since it's array like.
Sometimes you don't want your function context to be changed or you wanna add your own arguments, you use bind.
For example when you add a listener for an event with jquery, when jquery calls your function, the this object will be the element. But sometimes you wanna do tricky stuff and change it:
var myElement = {
init : function () {
$(this.element).click(this.listener.bind(this));
},
view : "<li>${Name}</li>",
name : 'ed',
element : $('#myelement'),
listener : function () {
this.element.append($.tmpl( this.view, this ));
}
}
myElement.init();
So here, you bind it to the myElement, so you can have access to the object properties to render the view. Another examples would be the following:
for (var i = 0; i < 10; i++) {
setTimeout(function () {console.log(i)}, 10)
}
// All of them will be 10.
for (var i = 0; i < 10; i++) {
setTimeout((function () {console.log(this.i)}).bind({ i : i }, 10)
}
If you have put an asynchronous function call in a loop, by the time the callback is called, the loop is finished, and the counter have reached the end, you can use bind to cleanly bind the current counter to your callback.
Another good use case of it, that I use a lot is when passing my functions with arguments to async module, without creating closures.
async.parallel({
writeFile : function (cb) {
fs.writeFile('lolz.txt', someData, cb);
},
writeFile2 : function (cb) {
fs.writeFile('lolz2.txt', someData, cb);
}
}, function (err){
console.log('finished')
});
async.parallel({
writeFile : fs.writeFile.bind(fs, 'lolz.txt', someData),
writeFile2 : fs.writeFile.bind(fs, 'lol2z.txt', someData),
}, function (err){
console.log('finished')
});
These two implementations are identical.
Performance
Just check these out:
http://jsperf.com/bind-vs-call2
http://jsperf.com/js-bind-vs-closure/2
http://jsperf.com/call-vs-closure-to-pass-scope/10
bind has a big performance overhead comparing to other types of calling, but make sure you don't sacrifice performance with maintainability with pre-mature optimizations.
Also you can have a look at this article.
I have a javascript plugin for a special image scroller. The scroller contains a bunch of timeout methods and a lot of variables with values set from those timeouts.
Everything works perfectly, but for the site I am working on it is required that the pages are loaded dynamically. The problem with this is when i for instance change the language on the site this is made by jquery load function meaning the content is dynamically loaded onto the site - AND the image slider aswell.
NOW here is the big problem! When I load the image slider for the second time dynamically all my previous values remains as well as the timers and everything else. Is there any way to clear everything in the javascript plugin as if it where like a page reload?
I have tried a lot of stuff so far so a little help would be much appreciated!
Thanks a lot!
You might want something like that to reload scripts:
<script class="persistent" type="text/javascript">
function reloadScripts()
{ [].forEach.call(document.querySelectorAll('script:not(.persistent)'), function(oldScript)
{
var newScript = document.createElement('script');
newScript.text = oldScript.text;
for(var i=0; i<oldScript.attributes.length; i++)
newScript.setAttribute(oldScript.attributes[i].name, oldScript.attributes[i].value);
oldScript.parentElement.replaceChild(newScript, oldScript);
});
}
// test
setInterval(reloadScripts, 5000);
</script>
As far as I know, there's no other way to reset a script than completely remove the old one and create another one with the same attributes and content. Not even clone the node would reset the script, at least in Firefox.
You said you want to reset timers. Do you mean clearTimeout() and clearInterval()? The methods Window.prototype.setTimeout() and Window.prototype.setInterval() both return an ID wich is to pass to a subsequent call of clearTimeout(). Unfortunately there is no builtin to clear any active timer.
I've wrote some code to register all timer IDs. The simple TODO-task to implement a wrapper callback for setTimeout is open yet. The functionality isn't faulty, but excessive calls to setTimeout could mess up the array.
Be aware that extending prototypes of host objects can cause undefined behavior since exposing host prototypes and internal behavior is not part of specification of W3C. Browsers could change this future. The alternative is to put the code directly into window object, however, then it's not absolutely sure that other scripts will call this modified methods. Both decisions are not an optimal choice.
(function()
{ // missing in older browsers, e.g. IE<9
if(!Array.prototype.indexOf)
Object.defineProperty(Array.prototype, 'indexOf', {value: function(needle, fromIndex)
{ // TODO: assert fromIndex undefined or integer >-1
for(var i=fromIndex || 0; i < this.length && id !== window.setTimeout.allIds[i];) i++;
return i < this.length ? i : -1;
}});
if(!Array.prototype.remove)
Object.defineProperty(Array.prototype, 'remove', { value: function(needle)
{ var i = this.indexOf(needle);
return -1 === i ? void(0) : this.splice(i, 1)[0];
}});
// Warning: Extensions to prototypes of host objects like Window can cause errors
// since the expose and behavior of host prototypes are not obligatory in
// W3C specs.
// You can extend a specific window/frame itself, however, other scripts
// could get around when they call window.prototype's methods directly.
try
{
var
oldST = setTimeout,
oldSI = setInterval,
oldCT = clearTimeout,
oldCI = clearInterval
;
Object.defineProperties(Window.prototype,
{
// TODO: write a wrapper that removes the ID from the list when callback is executed
'setTimeout':
{ value: function(callback, delay)
{
return window.setTimeout.allIds[window.setTimeout.allIds.length]
= window.setTimeout.oldFunction.call(this, callback, delay);
}
},
'setInterval':
{ value: function(callback, interval)
{
return window.setInterval.allIds[this.setInterval.allIds.length]
= window.setInterval.oldFunction.call(this, callback, interval);
}
},
'clearTimeout':
{ value: function(id)
{ debugger;
window.clearTimeout.oldFunction.call(this, id);
window.setTimeout.allIds.remove(id);
}
},
'clearInterval':
{ value: function(id)
{
window.clearInterval.oldFunction.call(this, id);
window.setInterval.allIds.remove(id);
}
},
'clearTimeoutAll' : { value: function() { while(this.setTimeout .allIds.length) this.clearTimeout (this.setTimeout .allIds[0]); } },
'clearIntervalAll': { value: function() { while(this.setInterval.allIds.length) this.clearInterval(this.setInterval.allIds[0]); } },
'clearAllTimers' : { value: function() { this.clearIntervalAll(); this.clearTimeoutAll(); } }
});
window.setTimeout .allIds = [];
window.setInterval .allIds = [];
window.setTimeout .oldFunction = oldST;
window.setInterval .oldFunction = oldSI;
window.clearTimeout .oldFunction = oldCT;
window.clearInterval.oldFunction = oldCI;
}
catch(e){ console.log('Something went wrong while extending host object Window.prototype.\n', e); }
})();
This puts a wrapper method around each of the native methods. It will call the native functions and track the returned IDs in an array in the Function objects of the methods. Remember to implement the TODOs.