If I have two handlers attached to the same event, is there a way to ensure that one of them will always complete before the other.
Just for this example, I'm using a timeout to simulate a long operation. But could I have something like this:
var change_label = function () {
var option = $(".selected_option a").text();
$("li:first span").text(option);
};
$("#container").on("click", "li", function () {
var self = $(this);
var t = setTimeout(function () {
$(".sel").children("li").removeClass("selected_option");
self.addClass("selected_option");
}, 1000);
});
$("#container").on("click", "li", function () {
change_label();
});
and be sure that the text wouldn't be changed (by the second handler) until the class was applied by the first?
http://jsbin.com/ayihiv/1/edit
Here's an approach you might find interesting.
jQuery's Callbacks utility allows you to do some "loose coupling". In other words, you can have the setTimeout function stimulate an action or set of actions, without knowing what those actions are; they can be specified somewhere else in the code, the only requirement being that the same Callbacks queue is within scope of both blocks of code.
This is effectively a "pub/sub" pattern, although we are using it here to do something other than to publish and subscribe.
var cb = $.Callbacks('unique');
$("#container").on("click", "li", function() {
var self = $(this);
var t = setTimeout(function() {
$(".sel").children("li").removeClass("selected_option");
self.addClass("selected_option");
cb.fire(self.find("a").text());//<<<<<< stimulate any functions currently in the callbacks list cb, and pass the required text to them.
}, 1000);
});
var change_label = function(txt) {
$("li:first span").text(txt);
};
var log_it = function(txt) {
console.log('Text changed to: ' + txt);
};
var alert_it = function(txt) {
alert('Text changed to: ' + txt);
};
cb.add(change_label);
//cb.add(log_it);
//cb.add(alert_it);
DEMO. Try uncommenting the last two lines and you will see that additional things happen when the 1 second delay is up.
Related
I have 2 js files. In the first one I have this:
var functionName = "video";
var cont = 1;
$(function() {
window.control = function control() {
var tipo1 = functionName + cont + "();";
var tipo2 = eval(tipo1);
tipo2;
cont++;
});
In the second one:
function video1() {
control();
}
function video2() {
control();
}
The first time was fine, but in the second, first execute video1() and then video2(), why?
Your definition is wrong:
window.control = function control() {
I imagine because of this it's firing control() execution.
Change this to:
window.control = function() {
Also I see no reason for defining this function at DOM ready state. It will just cause confusion and potential reference issues. The definition of a function is only ran at execution point, these should potentially be on DOM ready state depending on their use.
I am quite interested when I run this simple function
$(window).resize(function() {
var that = $(this);
var widthValue = that.width();
console.log(widthValue + 'px');
});
It works when I start resizing my browser window.
But when I do this
$(window).resize(function() {
var that = $(this);
var widthValue = that.width();
console.log(widthValue + 'px');
}).resize();
It acts like load();. I added resize() at the end.
What is this called? Not sure I understand why and how this works.
The technique is called Chaining.
It boils down to a function returning this at the end, so you can call another method of the same object by chaining the method calls one after the other.
var foo = {
count: 0,
up: function () { this.count++; return this; },
show: function () { alert(this.count); return this; }
}
foo.show().up().show().up().up().show();
In this particular example, the resize method is overloaded. If you give it a function argument then it will bind that function as an event handler. If you call it without arguments, then it will trigger that event instead.
With the 2nd case, you are invoking or triggering the resize event.
Situation : I have an event listener on an item. When I press on it, it calls a method that will perform a webkitAnimation and I return the end of the animation as a result.
Problem : If I click several times on my item, the webkit animation's listener is not reset, so I get many callbacks ..
I tried to use removeEventListener but it doesn't work..
Thanks in advance!
var Test = (function () {
function Test(listItem) {
this.listItem = listItem;
listItem.addEventListener('click', function(event) {
this.startAnim(function() {
});
}
}
Test.prototype.startAnim = function(callback) {
this.listItem.style.webkitAnimationName = 'simpleAnim';
this.listItem.style.webkitAnimationDuration = '220ms';
this.listItem.addEventListener('webkitAnimationEnd', function() {
this.style.webkitAnimationName = '';
// This calls my callback too many times..
callback();
// the following doesn't work!
this.removeEventListener('webkitAnimationEnd', function() {
// this doesn't work....
}, false);
}, false);
};
return Test;
}
You have to remove the same function you added; the browser can't guess what function you mean to remove (as there can be many functions added). You're removing two different functions created at different times, so of course it doesn't work. Remember a reference to the function you added, and then remove that function.
E.g.:
Test.prototype.startAnim = function(callback) {
this.listItem.style.webkitAnimationName = 'simpleAnim';
this.listItem.style.webkitAnimationDuration = '220ms';
// Add a specific function
this.listItem.addEventListener('webkitAnimationEnd', animationEndHandler, false);
function animationEndHandler() {
this.style.webkitAnimationName = '';
// This calls my callback too many times..
callback();
// Remove the same specific function
this.removeEventListener('webkitAnimationEnd', animationEndHandler, false);
}
};
I've searched and searched and I'm coming up empty on this one guys. Help! I'm watching an input field (#filter_query) for keyup events using JQuery on(). When the user punches in some text into this input field the on() triggers a setTimeout() which then in turns triggers a search function for the site. I'm trying to avoid the search from being fired if the user continues to type into the input field thus the clearTimeout(). For some reason the timeout ID isn't being preserved for clearTimeout and a new ID is being generated each time.
Here's my code:
$(document).on('keyup', '#filter_query', function (event) {
var iTimeoutID,
iTypingDelay = 800,
sFilterVal = $.trim($('#filter_query').val()),
sFilterCat = $('#filter_catagory').val(),
sFilterCol = $('#filter_col').val();
clearTimeout(iTimeoutID);
if (sFilterVal !== "") {
iTimeoutID = setTimeout(
function () {
searchFunction();
},
iTypingDelay
);
}
});
With the above code my search function is being fired off several times regardless of the clearTimeout(). Thank you!
Your iTimeoutID is a local variable to the .on() event handler function and thus it's destroyed each time that function completes and recreated the next time. Move it out of that scope to a higher level or to the global level so it can survive from one event to the next.
You can do it like this;
var iTimeoutID = null;
$(document).on('keyup', '#filter_query', function (event) {
var iTypingDelay = 800,
sFilterVal = $.trim($('#filter_query').val()),
sFilterCat = $('#filter_catagory').val(),
sFilterCol = $('#filter_col').val();
if (iTimeoutID) {
clearTimeout(iTimeoutID);
iTimeoutID = null;
}
if (sFilterVal !== "") {
iTimeoutID = setTimeout(function() {
iTimeoutID = null;
searchFunction();
}, iTypingDelay);
}
});
If you want to avoid global variables or you have more than one of these, you can use jQuery's .data() to store the timer id on the object like this:
$(document).on('keyup', '#filter_query', function (event) {
var self = $(this);
var iTimeoutID = self.data("timerID") || null;
var iTypingDelay = 800,
sFilterVal = $.trim($('#filter_query').val()),
sFilterCat = $('#filter_catagory').val(),
sFilterCol = $('#filter_col').val();
if (iTimeoutID) {
clearTimeout(iTimeoutID);
iTimeoutID = null;
}
if (sFilterVal !== "") {
iTimeoutID = setTimeout(function() {
self.data("timerID", null);
searchFunction();
}, iTypingDelay);
}
self.data("timerID", iTimeoutID);
});
Here's one other version that uses a self-executing function to act as a shell for some variables that can last across the event handlers without being global:
(function() () {
var iTimeoutID = null;
$(document).on('keyup', '#filter_query', function (event) {
var iTypingDelay = 800,
sFilterVal = $.trim($('#filter_query').val()),
sFilterCat = $('#filter_catagory').val(),
sFilterCol = $('#filter_col').val();
if (iTimeoutID) {
clearTimeout(iTimeoutID);
iTimeoutID = null;
}
if (sFilterVal !== "") {
iTimeoutID = setTimeout(function() {
iTimeoutID = null;
searchFunction();
}, iTypingDelay);
}
});
})();
If this was something quick and there was little chance of conflict with other code and there's only one object being served by the event handler, the first option is perfectly fine.
If the .on() event handler served multiple objects and each needed to keep track of it's own state, the 2nd option is perfect for that.
If you don't have multiple objects on the same event handler, then the third option is the simplest way to keep from adding any new global variables.
Because you are defining the variable var iTimeoutID, that means that it's not a global variable and only accessible inside that function. So when the function gets called again, it creates a new variable.
I believe you should be able to fix it by not declaring the variable var iTimeoutID.
I might be wrong so if so, someone please correct me.
I want my handler of the ready event will fire after all other handlers are done.
It's extremely handy for manipulating plugins' undesired actions.
If I write my handler after all others, it only guarantees it will fire after all others fired, not finished:
$(function() {
setTimeout(function() { alert('other handler'); }, 500);
});
$(function() { alert('my handler'); });
Fiddle
In that code, my handler alerted first.
I read that before jQuery version 1.4 the readyList was public. so in version 1.7 I have no idea how I can tell that my handler is the last handler or not.
If the idea is that you don't control the other ready handlers, then given your example where another handler used a setTimeout, you can never actually know (without inspecting the other code) if your code will run after all other code.
The readyList wouldn't help even if it was public, because in your example, the handler with the setTimeout will be removed from the readyList long before the setTimeout handler runs. The readyList Array doesn't have any control over that sort of asynchronous code either.
So if you don't control (can't modify) the other code, then I really don't have a solution. But if the other code is just long running, but not asynchronous, then there wouldn't be any issue, because if your code is the last .ready() handler assigned, it shouldn't matter how long the other handlers take to execute. If their code is synchronous, it will force yours to wait until they're complete. It's just that if they're using asynchronous code, like your setTimeout example, then there's nothing you can do short of examining the other code, and modifying yours to make sure it fires last.
You can use something like this:
function Join(cb) {
var paths = 0;
var triggerCallback = cb;
this.add = function () {
paths ++;
return this.call;
};
this.call = function () {
paths --;
if (paths == 0)
if (triggerCallback)
triggerCallback();
};
return this;
}
An example:
function finishedAll() {
alert("All finished");
}
window.join = new Join(finishedAll);
function sampleCall(callJoinHandle) {
alert("Not done yet.");
if (callJoinHandle) callJoinHandle();
}
var cb1 = join.add();
setTimeout(function () { sampleCall(cb1); }, 1000);
var cb2 = join.add();
setTimeout(function () { sampleCall(cb2); }, 1000);
var cb3 = join.add();
setTimeout(function () { sampleCall(cb3); }, 1000);
An idea could be creating an array of deferred to use inside every ready function (except the last one), resolving each one when the snippet has completed.
Then, in the last ready function you could simply check the promise resolution with $.when and then execute some other code: e.g.
var dfdArray = [];
$(function() {
var dfd = $.Deferred();
dfdArray.push(dfd);
setTimeout(function() {
console.log('another simple handler');
dfd.resolve();
}, 2000);
});
$(function() {
var dfd = $.Deferred();
dfdArray.push(dfd);
setTimeout(function() {
console.log('first handler');
dfd.resolve();
}, 1200);
});
$(function() {
$.when.apply($, dfdArray).done(function() {
alert('my final handler');
})
});
See fiddle in action here: http://jsfiddle.net/DXaw5/
I don't know if it is possible for you to create a queue for all the functions like
var queue = [];
queue .push(fun1);
queue .push(fun2);
//execute the first function and remove it.
(queue .shift())();
I usually use the following pattern, simply keepig a counter of finished async functions:
var fired = 10;
var finished = 0;
for (var i = 0; i < fired; i++) {
// Call an asynchronous function 10 times
async_function(function() {
// When asynchronous function finishes,
// we check if it was the last one.
if (++finished == fired) all_ready();
});
}
The same in coffeescript:
fired = 10
finished = 0
(async_function -> all_ready() if ++finished == ready) for n in [0...fired]
(We call the same function for 10 times to keep the example simple, while in reality you may of course call different functions, but the same idea apply; in callback function you check the counter.)