2010-07-15 20 views
11

En mi solicitud escucho la API de Google Maps 'bounds_changed' evento para enviar una petición AJAX para actualizar algunos div en la página web en función de los nuevos límites del mapa:cómo responder a un evento Javascript solo si se dispara una vez y luego no se dispara nuevamente durante un período de tiempo?

google.maps.event.addListener(map, 'bounds_changed', function() { 
    // here goes an ajax call 
} 

El evento 'bounds_changed' es disparado con una alta frecuencia cuando el usuario arrastra el mapa. Tanto que hay demasiadas solicitudes ajax enviadas al servidor.

Básicamente me gustaría hacer la llamada ajax solo después de que el usuario se haya detenido para mover el mapa durante un período de tiempo (por ejemplo, 500 ms). No tengo mucha experiencia con Javascript y traté de lograr esto con setTimeout y clearTimeout, pero sin éxito.

Cualquier idea sería apreciada :)

+0

gracias a todos por sus respuestas! – Florent2

Respuesta

12

Añadir un tiempo de espera, que ejecuta las 500 ms después de código se activa el evento, cada vez que se activa el evento borrar el tiempo de espera y crear una nueva.

por ejemplo.

google.maps.event.addListener(map, 'bounds_changed', (function() { 
    var timer; 
    return function() { 
     clearTimeout(timer); 
     timer = setTimeout(function() { 
      // here goes an ajax call 
     }, 500); 
    } 
}())); 
+0

+1, aunque debe separar las preocupaciones y sacar la función como una utilidad. – gradbot

+0

truco muy ingenioso (con las funciones de triple anidado) pero difícil de leer y seguir la intención del código y, por lo tanto, es más difícil de mantener, a largo plazo. – gMale

+0

Cierto, si fuera a usarse más de una vez, probablemente llevaría los 2 niveles externos a una biblioteca, como sugiere Gradbot. Entonces sería una función simple que toma una devolución de llamada. –

0

Una solución rápida y sucia sería llamar al servidor con menos frecuencia:

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) 
    } 
}); 

Alternativamente, he hecho algo similar en el que he restablecer una temporizador cada vez que "escuché" del usuario. Una vez que el temporizador expiró, llamó a mi acción. Entonces, el temporizador intenta continuamente apagarse, pero si el usuario hace algo, el temporizador se reinicia. Finalmente, el usuario deja de moverse lo suficiente para que el temporizador tenga la oportunidad de desencadenar su evento.


EDIT:
A lo largo de las líneas de:

google.maps.event.addListener(map, 'bounds_changed', userInput); 

function userInput() { 
    resetTimer(); 
} 

Dónde resetTimer() despeja/inicia el temporizador. Eso sería algo así como:

var inputTimer = null; 
var timeLimit = 500; 

function resetTimer() { 
    if(inputTimer != null) clearInterval(inputTimer); 
    inputTimer = setTimeout('contactServer()', timeLimit); 
} 

function contactServer() { 
    // here goes an ajax call 
} 

No he probado que esto compila, pero debería darle la idea básica. Este código tiene la ventaja de ser lo suficientemente modular como para funcionar en muchos otros idiomas con solo ediciones menores. Sigo una lógica similar en ActionScript. Además, es extremadamente fácil de leer, seguir lógicamente y mantener (6 meses más tarde, cuando te olvidas de cómo funciona tu código).

Yo espero que ayude de alguna manera,

--gMale

+0

javascript no compila ^^ – Marko

+0

@Marko seguro, V8 en Chrome JITs el código. '^. ^' – gradbot

2

Este código se asegurará de que ha sido medio segundo desde que el evento fue el último despedido antes de hacer su cosa (el comentado TODO). Creo que esto es lo que quieres.

var mapMoveTimer; 
google.maps.event.addListener(map, 'bounds_changed', function(){ 
    clearTimeout(mapMoveTimer); 
    mapMoveTimer = setTimeout(function(){ 
    // TODO: stuff with map 
    }, 500); 
}); 
+0

funciona pero usa un – gradbot

+1

global que solo importa si existe la posibilidad de que otro script haya creado un archivo global con el mismo nombre. Si se trata de una preocupación, todo el asunto podría estar envuelto en un cierre, pero si solo se trata de secuencias de comandos únicas para una sola página web, no importará en absoluto. –

4

Hay un muy buen enfoque disponible en 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); 
    }; 
} 

Esto permitirá hacer:

// 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)); 

Si prefiere no aumentar el Function.prototype hay un independiente function debounce(func, threshold, execAsap) disponible en el blog post.

+0

+1, bastante bueno que mantiene la referencia del objeto y permite argumentos. – gradbot

2

Este es el código de Brenton Alker pero se movió a una función de utilidad.

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 
})); 
+0

Ahorrador de vida. Gracias. – mapr

3

Google sugiere el uso de otro oyente ...

google.maps.event.addListener(map, 'idle', showMarkers); 

cita "Tenga en cuenta que se podía escuchar el evento bounds_changed pero dispara continuamente a medida que las cacerolas de los usuarios, sino que la inactividad se disparará una vez el usuario ha dejado de panoramizar/acercar ". /presupuesto

ver

http://code.google.com/apis/maps/articles/toomanymarkers.html#gridbasedclustering

+0

funciona como un amuleto ... la misma sensación que con el temporizador de .5 segundos – camel

Cuestiones relacionadas