I have a search input that listens to keyup
and change
to trigger an update of a listview via Ajax.
Looks like this:
input.on('keyup change', function(e) {
if (timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout( function() {
timer = null;
val = input.val();
el = input.closest('ul');
// run a function - triggers Ajax
widget[func](dyn, el, lib_template, locale, val, "update");
}, interval );
});
All working nice, except the handling of the timeout and binding, which causes double Ajax requests to be placed instead of a single one (when the keyup
has passed, the change
event triggers the same Ajax request again).
I can "fix" this by adding another timeout:
var runner = false;
input.on('keyup change', function(e) {
if ( runner === false ){
runner = true;
if (timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout( function() {
timer = null;
val = input.val();
el = input.closest('ul');
widget[func](dyn, el, lib_template, locale, val, "update");
// ssh....
window.setTimeout( function(){ runner = false; },2500);
}, interval );
}
});
But this is not nice at all...
Question:
How can I make sure with two binding that both fire, that the function I need only runs once?
Thanks!
EDIT:
The Ajax call is triggered here:
widget[func](dyn, el, lib_template, locale, val, "update");
which calls this function to build a dynmic listview
buildListView : function( dyn,el,lib_template,locale,val,what ){
...
// this calls my AJax Config "getUsers"
$.parseJSON( dynoData[ dyn.method ](cbk, val, dyn.display) );
});
// config AJAX
getUsers: function(cbk, val, recs){
var form = "",
pullRetailers = ( val === undefined ? "" : val ),
service = "../services/some.cfc",
method = "by",
returnformat = "json",
targetUrl = "",
formdata = "...manually_serialized...,
successHandler = function(objResponse, cbk) {
cbk( objResponse );
};
// finally pass to the generic JSON handler
ajaxFormSubmit( form, service, formdata, targetUrl, successHandler, "yes", "", returnformat, cbk );
}
// generic AJAX
var ajaxFormSubmit =
function ( form, service, formdata, targetUrl, successHandler, dataHandler, errorHandler, returnformat, type ){
...
$.ajax({
async: false,
type: type == "" ? "get" : type,
url: service,
data: formdata,
contentType: 'application/x-www-form-urlencoded',
dataType: returnformat,
success: function( objResponse ){
if (objResponse.SUCCESS == true || typeof objResponse === "string" ){
dataHandler == "yes" ? successHandler( objResponse, override ) : successHandler( override );
}
},
error: function (jqXHR, XMLHttpRequest, textStatus, errorThrown) { }
});
}
But this does not help a lot regarding the actual question of how to prevent both events from triggering my Ajax Update.
I would try to set up a value-checking function like this:
var $inputIntance = $("#path-to-your-input");
var lastInputValue;
function checkInputValue () {
var newValue = $inputIntance.val();
if (newValue != lastInputValue) {
// make your AJAX call here
lastInputValue = newValue;
el = $inputIntance.closest('ul');
widget[func](dyn, el, lib_template, locale, lastInputValue, "update");
}
}
and then then fire this checks by any user-action event you like:
$inputIntance.on('keyup change', function(e) {
checkInputValue();
}
or like this
$inputIntance.on('keyup change', checkInputValue );
UPDATE: there might be the case when you have to limit the number of AJAX requests per time. I added time control functionality to my previous code. You can find the code below and try it live here in JSFiddle.
$(document).ready(function () {
var $inputIntance = $("#test-input");
var lastInputValue;
var valueCheckTimer;
var MIN_TIME_BETWEEN_REQUESTS = 100; //100ms
var lastCheckWasAt = 0;
function checkInputValue () {
lastCheckWasAt = getTimeStamp();
var newValue = $inputIntance.val();
if (newValue != lastInputValue) {
// make your AJAX call here
lastInputValue = newValue;
$("#output").append("<p>AJAX request on " + getTimeStamp() + "</p>");
//el = $inputIntance.closest('ul');
//widget[func](dyn, el, lib_template, locale, lastInputValue, "update");
}
}
function getTimeStamp () {
return (new Date()).getTime();
}
function checkInputValueScheduled() {
if (valueCheckTimer) { // check is already planned: it will be performed in MIN_TIME_BETWEEN_REQUESTS
return;
} else { // no checks planned
if ((getTimeStamp() - lastCheckWasAt) > MIN_TIME_BETWEEN_REQUESTS) { // check was more than MIN_TIME_BETWEEN_REQUESTS ago
checkInputValue();
} else { // check was not so much time ago - schedule new check in MIN_TIME_BETWEEN_REQUESTS
valueCheckTimer = window.setTimeout(
function () {
valueCheckTimer = null;
checkInputValue();
},
MIN_TIME_BETWEEN_REQUESTS
);
}
}
}
$inputIntance.bind('keyup change', function(e) {
$("#output").append("<p>input event captured</p>");
checkInputValueScheduled();
});
});
I came across this problem before and what I ended up doing is saving the request on an object upon creation and test for a previously set one to abort it if necessary. You'd have to edit the function that triggers the ajax call. For example:
if ( ajaxRequest ) {
ajaxRequest.abort();
}
ajaxRequest = $.ajax({ ... }); // triggers ajax request and saves it
If the widget
function returns an ajax object then you can probably assign that to the variable instead of modifying the original ajax request.
Any reason for listening to keyUp
in addition to change
? Maybe what really matters is just one of these events.
If not, I guess you will have to use a closure as you suggested.
Ok. I got it (after a felt 1000 tries...).
Here is what works = triggers whichever event fires first, blocks the trailer:
var isEven;
input.on('keyup change', function(e) {
isEven = true;
if (timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout( function() {
timer = null;
val = input.val();
el = input.closest('ul');
// prevent double firing after interval passed
if (isEven === true ){
isEven = false;
} else {
isEven = true;
return;
}
widget[func](dyn, el, lib_template, locale, val, "update");
}, interval );
});
Seems to do what it should. Feel free to correct me, if it's wrong.
来源:https://stackoverflow.com/questions/13900093/how-to-run-a-function-once-when-binding-to-multiple-events-that-all-trigger-in-j