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.
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 have a module that has to recording functions I want to add. My problem is that because this.audio.stdout has a listener set for another function, I can only remove the listener activated when the start function is called without screwing up other processes. Because the value of filename changes based on when the function was called I have to define the callback in scope of when that value was set. This works for beginning recording with start() but when I call stop(), which removes the listener, the program doesn't know what to do because the callback is out of scope. What would be the proper way to do this?
function Record(rx) {
this.rx = rx;
this.audio = spawn('audio_client');
}
Record.prototype.start = function () {
var self = this;
self.filename= new Date().getTime()+'_'+this.rx
function record(data) {
console.log(self.filename);
}
this.audio.stdout.on('data', record);
}
Record.prototype.stop = function () {
this.audio.stdout.removeListener('data',record);
}
UPDATE:
Sorry I didn't understand what you were asking at first. I looked at this for a bit and this is the best I could come up with. It's not ideal to create the record method for each instance in the constructor like this, but again, this is the best I could come up with.
function Record(rx) {
this.rx = rx;
this.audio = spawn('audio_client');
var self = this;
this.record = function (data) {
console.log(self.filename);
};
}
Record.prototype.start = function () {
this.filename= new Date().getTime()+'_'+this.rx
this.audio.stdout.on('data', this.record);
};
Record.prototype.stop = function () {
this.audio.stdout.removeListener('data', this.record);
};
UPDATE #2:
Better still since you are specific to node, would be this.record = this.record.bind(this);.
I am trying to pass "this" from:
myVar = setInterval("displayDate(this )",1000);
and is passing "div.test" like it should when I step through it, but when receiving it in:
function displayDate(obj){
}
It says that it is "Window"??? Below is the JavaScript I am building. I am trying to build a foundation for classes that trigger events and eventually I am going to be changing the elements.src=".jpg" by a variable rate (now set to 100) through Sprite parsing. But I am currently stuck on this and I don't want to have to insert onmousemove attributes, etc. in the .html code to keep it clean. . . keep in mind this is only my third day writing .html/.css/.js so any help is appreciated!
// This helps create a static variable that isn't polluting the global namespace
var incr = (function () {
var i = 0;
return function(){ return i++; };
})();
// This perform all of the functions that we would like with some error handling
function displayDate(obj){
var counter = incr();
try{
obj.innerHTML=counter;
}catch(err){
var txt="There was an error on this page.\n\n";
txt+="Error description: " + err.message + "\n\n";
txt+="Click OK to continue.\n\n";
alert(txt);
}
}
// This is our trigger that sets an interval for our main Java function
$(function(){
var myVar;
$(".test").hover( function() {
// The mouse has entered the element, can reference the element via 'this'
myVar = setInterval("displayDate(this )",100);
},function () {
// The mouse has left the element, can reference the element via 'this'
clearInterval(myVar);
}
);
});
The time the displayDate function is called, your into another scope and this is your window object (not the div element anymore). To resolve, you can do like this:
$(".test").hover( function() {
var self = this;
myVar = setInterval(function() {
displayDate(self);
},1000);
}, function() {
clearInterval(myVar);
});
instead of setInterval("displayDate(this )",100);
$(".test").hover( function() {
var that = $(this);
setInterval(function () {
displayDate(that);
},100);
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);
}
};
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.