Как реагировать на событие Javascript, только если оно запускается один раз, а затем не запускается снова в течение некоторого периода времени?
В моем приложении я слушаю событие Google Maps API "bounds_changed", чтобы отправить ajax-запрос на обновление некоторого элемента div на веб-странице в зависимости от новых границ карты:
google.maps.event.addListener(map, 'bounds_changed', function() {
// here goes an ajax call
}
Событие 'bounds_changed' запускается с высокой частотой, когда пользователь перетаскивает карту. Настолько, что на сервер отправлено слишком много запросов AJAX.
В основном я хотел бы сделать вызов ajax только после того, как пользователь перестал перемещать карту в течение некоторого периода времени (например, 500 мс). Я не очень опытен с Javascript и пытался добиться этого с помощью setTimeout и clearTimeout, но безуспешно.
Любая идея будет оценена :)
Ответы
Ответ 1
Добавьте тайм-аут, который запускает ваш код 500 мс после возникновения события, каждый раз, когда событие очищает тайм-аут и создает новый.
например.
google.maps.event.addListener(map, 'bounds_changed', (function () {
var timer;
return function() {
clearTimeout(timer);
timer = setTimeout(function() {
// here goes an ajax call
}, 500);
}
}()));
Ответ 2
На unscriptable.com:
Function.prototype.debounce = function (threshold, execAsap) {
var func = this, // reference to original function
timeout; // handle to setTimeout async task (detection period)
// return the new debounced function which executes the original function
// only once until the detection period expires
return function debounced () {
var obj = this, // reference to original context object
args = arguments; // arguments at execution time
// this is the detection function. it will be executed if/when the
// threshold expires
function delayed () {
// if we're executing at the end of the detection period
if (!execAsap)
func.apply(obj, args); // execute now
// clear timeout handle
timeout = null;
};
// stop any current detection period
if (timeout)
clearTimeout(timeout);
// otherwise, if we're not already waiting and we're executing at the
// beginning of the waiting period
else if (execAsap)
func.apply(obj, args); // execute now
// reset the waiting period
timeout = setTimeout(delayed, threshold || 100);
};
}
Это позволит вам сделать:
// call the function 200ms after the bounds_changed event last fired:
google.maps.event.addListener(map, 'bounds_changed', (function() {
// here goes an ajax call
}).debounce(200));
// call the function only once per 200ms:
google.maps.event.addListener(map, 'bounds_changed', (function() {
// here goes an ajax call
}).debounce(200,true));
Если вы предпочитаете не увеличивать Function.prototype
, существует автономный function debounce(func, threshold, execAsap)
, доступный в сообщении .
Ответ 3
Google предлагает использовать другой прослушиватель...
google.maps.event.addListener(map, 'idle', showMarkers);
котировка
"Обратите внимание, что вы можете прослушивать событие bounds_changed, но он непрерывно срабатывает, когда пользователь нажимает, вместо этого простаивает, когда пользователь прекратил панорамирование/масштабирование".
/Цитата
см
http://code.google.com/apis/maps/articles/toomanymarkers.html#gridbasedclustering
Ответ 4
Этот код обеспечит, что прошло полсекунды, так как последнее событие было уволено, прежде чем делать свою вещь (прокомментированный TODO
). Я думаю, что это то, что вы хотите.
var mapMoveTimer;
google.maps.event.addListener(map, 'bounds_changed', function(){
clearTimeout(mapMoveTimer);
mapMoveTimer = setTimeout(function(){
// TODO: stuff with map
}, 500);
});
Ответ 5
Это код Brenton Alker, но перешел в служебную функцию.
var frequencyReduce = function(delay, callback){
var timer;
return function(){
clearTimeout(timer);
timer = setTimeout(callback, delay);
};
};
google.maps.event.addListener(map, 'bounds_changed', frequencyReduce(500, function(){
// here goes an ajax call
}));
Ответ 6
Одним быстрым и грязным решением было бы называть сервер реже:
var requestCounter = 0;
var frequency = 50;
google.maps.event.addListener(map, 'bounds_changed', function() {
if((++requestCounter % frequency) == 0){
// here goes an ajax call (also, optionally reset the counter here)
}
});
В качестве альтернативы, я сделал что-то подобное, когда я reset таймер каждый раз, когда я "слышал" от пользователя. Как только таймер истек, он вызвал мое действие. Поэтому таймер постоянно пытается отключиться, но если пользователь что-то делает, таймер reset. В конце концов, пользователь перестает двигаться достаточно долго, чтобы таймер мог инициировать это событие.
EDIT:
По строкам:
google.maps.event.addListener(map, 'bounds_changed', userInput);
function userInput() {
resetTimer();
}
Если resetTimer() очищает/запускает ваш таймер. Это выглядит примерно так:
var inputTimer = null;
var timeLimit = 500;
function resetTimer() {
if(inputTimer != null) clearInterval(inputTimer);
inputTimer = setTimeout('contactServer()', timeLimit);
}
function contactServer() {
// here goes an ajax call
}
Я не тестировал, что это компилируется, но он должен дать вам основную идею. Преимущество этого кода в том, что он достаточно модульный, чтобы работать на многих других языках с незначительными изменениями. Я следую аналогичной логике в ActionScript. Кроме того, он очень легко читается, логически следит и поддерживает (6 месяцев спустя, когда вы забыли, как работает ваш код).
Я надеюсь, что это поможет в некотором роде,
- gMale