2011-08-12 8 views
54

¿Es posible saber si un evento de desplazamiento fue realizado por el navegador o por el usuario? Específicamente, al usar el botón Atrás un navegador puede saltar a la última posición de desplazamiento conocida. Si me enlace al evento scroll, ¿cómo puedo saber si esto fue causado por el usuario o el navegador?Detectar si el evento de desplazamiento fue creado por el usuario

$(document).scroll(function(){ 
    //who did this?! 
}); 

Veo tres tipos de situaciones que causan el desplazamiento en un navegador.

  1. El usuario realiza alguna acción. Por ejemplo, usa la rueda del mouse, las teclas de flecha, las teclas de página arriba/abajo, las teclas de inicio/final.
  2. El navegador se desplaza automáticamente. Por ejemplo, al usar el botón Atrás en su navegador, saltará automáticamente a la última posición de desplazamiento conocida.
  3. Scrolls de Javascript. Por ejemplo, element.scrollTo(x,y).
+1

No estoy seguro de su pregunta, si considera el salto utilizando el botón de fondo para mí un evento de desplazamiento del navegador o el usuario. En general: ¿Qué considera "desplazarse por el navegador"? Si se refiere al desplazamiento iniciado por su secuencia de comandos, entonces todo lo que necesita hacer es cuando su secuencia de comandos se desplaza, ya sea para desactivar el controlador de eventos o establecer una bandera para que el manejador de eventos sepa ignorarla. – RoToRa

+0

Considero que el desplazamiento a través del botón Atrás es un "desplazamiento del navegador". Cualquier otra cosa: rueda del mouse, flechas arriba/abajo, clic en el botón central, etc. sería un desplazamiento del usuario. Creo que mi verdadera pregunta puede ser: ¿hay alguna manera de diferenciar de dónde viene un evento? Miré las propiedades en el objeto del evento, pero no pude encontrar nada. Los tres escenarios que puedo imaginar son el desplazamiento iniciado por el navegador, el desplazamiento iniciado por JavaScript y el desplazamiento iniciado por el usuario. Espero que eso aclare las cosas. – mrtsherman

+0

@mrtsherman Encontré algunos de estos mientras obtenía el mismo resultado: http://stackoverflow.com/questions/2834667/how-can-i-differentiate-a-manual-scroll-via-mousewheel-scrollbar-from- a-javasc – AlphaMale

Respuesta

17

Desafortunadamente, no hay una manera directa de decir eso.

Yo diría que si puede rediseñe su aplicación para que no dependa de este tipo de flujo, vaya para eso.

Si no, una solución que puedo pensar es hacer un seguimiento de las volutas iniciadas por el usuario y verificar que el scroll fue activado por el navegador o por el usuario.

He aquí un ejemplo que preparé, que lo hace bastante bien (excepto en navegadores donde el historial de jQuery tiene problemas).

Necesita ejecutar esto localmente para poder probarlo completamente (jsFiddle/jsbin no son buenos ajustes ya que iFrame los contenidos).

Aquí está la casos de prueba que yo validado:

  • página se carga - userScroll es false
  • de desplazamiento a través del ratón/teclado - userScroll convierte true
  • Haga clic en el enlace para saltar a pie de página - userScroll se convierte en false
  • Haga clic en Atrás/Adelante - userScroll se convierte en false;

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="utf-8" /> 
    <script src="http://code.jquery.com/jquery-1.6.1.min.js"></script> 
    <script type="text/javascript" src="https://raw.github.com/tkyk/jquery-history-plugin/master/jquery.history.js"></script> 
</head> 
<body> 
    <span> hello there </span><br/> 
    <a href="#bottom"> click here to go down </a> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <a name="bottom"> just sitting </a> 
</body> 
<script type="text/javascript"> 

var userScroll = false;  

function mouseEvent(e) { 
    userScroll = true; 
} 


$(function() { 

    // reset flag on back/forward 
    $.history.init(function(hash){ 
     userScroll = false; 
    }); 

    $(document).keydown(function(e) { 
     if(e.which == 33  // page up 
      || e.which == 34  // page dn 
      || e.which == 32  // spacebar 
      || e.which == 38  // up 
      || e.which == 40  // down 
      || (e.ctrlKey && e.which == 36)  // ctrl + home 
      || (e.ctrlKey && e.which == 35)  // ctrl + end 
     ) { 
      userScroll = true; 
     } 
    }); 

    // detect user scroll through mouse 
    // Mozilla/Webkit 
    if(window.addEventListener) { 
     document.addEventListener('DOMMouseScroll', mouseEvent, false); 
    } 

    //for IE/OPERA etc 
    document.onmousewheel = mouseEvent; 


    // to reset flag when named anchors are clicked 
    $('a[href*=#]').click(function() { 
     userScroll = false; 
    }); 

     // detect browser/user scroll 
    $(document).scroll(function(){ 
     console.log('Scroll initiated by ' + (userScroll == true ? "user" : "browser")); 
    }); 
}); 
</script> 
</html> 

Notas:

  • Esto no hace un seguimiento de desplazamiento cuando el usuario arrastra la barra de desplazamiento con el ratón. Esto se puede agregar con más código, que dejé como ejercicio para ti.
  • event.keyCodes puede variar según el sistema operativo, por lo que es posible que tenga que cambiarlo adecuadamente.

Espero que ayude!

+0

Gracias Mrchief. Creo que esta es la mejor respuesta a pesar de que no es lo que esperaba que fuera (¿Qué? ¡No hay propiedad 'event.thrower' ?!). – mrtsherman

2

Pruebe usar los eventos Mousewheel y DOMMouseScroll en su lugar. Ver http://www.quirksmode.org/dom/events/scroll.html

+0

Gracias por esta sugerencia. Sin embargo, no creo que esto tenga lo que quiero. De hecho, necesito diferenciar entre el navegador y el desplazamiento del usuario. No solo apunte a la rueda del mouse. – mrtsherman

+0

Creo que el evento mousewheel se dispara antes del evento onscroll. Por lo tanto, puede establecer var userscroll = true en la rueda del mouse y detectarlo en onscroll (y restablecerlo en false). – Gerben

+2

Hay una gran cantidad de eventos de desplazamiento iniciados por el usuario que no estarían cubiertos. Las teclas de flecha, el tamaño de la ventana, etc. Es mucho más seguro decir que el remitente de este evento es "X" que decir que todo lo que esté fuera de este evento debe ser "X". Tampoco funciona para mí, ya que quiero identificar un desplazamiento iniciado por el navegador, no por el usuario. Entonces para hacer este caso de uso tendría que seguir todos los rollos de la rueda del mouse y luego tratar de deducir si el evento de desplazamiento subsiguiente fue basado en eso por el usuario. Me temo que no es viable. – mrtsherman

6

Por lo que yo sé, es imposible (sin ningún trabajo) decir cada vez que el evento de desplazamiento haya sido emitido por "usuario" o por otros medios.

Podrías probar (como otros mencionaron) atrapar eventos de mouse, y luego probablemente intentar capturar evento keydown en cualquier tecla que pueda desencadenar scroll (flechas, espacio, etc.) mientras revisas qué elemento está enfocado actualmente, ya que por ejemplo puedes Desplácese utilizando las teclas de flecha mientras escribe en un campo de entrada. En general, sería una secuencia de comandos compleja y desordenada.

Dependiendo de la situación que está tratando con usted podría adivinar "revertir la lógica", y en lugar de la detección de eventos de desplazamiento emitida por el usuario simplemente conectar en cualquier desplaza hacia hechas mediante programación y tratar cualquier eventos de desplazamiento no hecha por su código como hecho por un usuario. Como dije, depende de una situación y de lo que intenta lograr.

+0

Gracias WTK. Esta fue la mejor respuesta por un tiempo, pero desafortunadamente para ti Mrchief expuso tu respuesta con algunos ejemplos de código así que le di la recompensa. ¡Si pudiera haberlo dividido lo hubiera hecho! – mrtsherman

+2

Eso está bien. No se trata de generosidad sino de difundir y adquirir conocimiento :) – WTK

1

Puede comprobar la posición de desplazamiento en listo. Cuando active el evento de desplazamiento, compruebe que la posición de desplazamiento sea diferente de la que tenía cuando se cargó la página. Por último, asegúrese de borrar el valor almacenado una vez que se desplaza la página.

$(function() { 
    var loadScrollTop = ($(document).scrollTop() > 0 ? $(document).scrollTop() : null); 
    $(document).scroll(function (e) { 
     if ($(document).scrollTop() !== loadScrollTop) { 
      // scroll code here! 
     } 
     loadScrollTop = null; 
    }); 
}); 
+0

Gracias por esta idea. Hay algunos escenarios que esto no cubre para mí. Aunque es una buena idea, la guardaré en mi bolsillo si alguna vez la necesito. – mrtsherman

0

Me pareció muy útil. Aquí hay una versión de coffeescript para aquellos tan interesados.

$ -> 
    S.userScroll = false 

    # reset flag on back/forward 
    if $.history? 
    $.history.init (hash) -> 
     S.userScroll = false 

    mouseEvent = (e)-> 
    S.userScroll = true 

    $(document).keydown (e) -> 
    importantKey = (e.which == 33 or  # page up 
     e.which == 34 or # page dn 
     e.which == 32 or # spacebar 
     e.which == 38 or # up 
     e.which == 40 or # down 
     (e.ctrlKey and e.which == 36) or # ctrl + home 
     (e.ctrlKey and e.which == 35) # ctrl + end 
    ) 
    if importantKey 
     S.userScroll = true; 

    # Detect user scroll through mouse 
    # Mozilla/Webkit 
    if window.addEventListener 
     document.addEventListener('DOMMouseScroll', mouseEvent, false); 

    # for IE/OPERA etc 
    document.onmousewheel = mouseEvent; 
2

Sí, es 100% posible. Actualmente uso esto en una aplicación donde IE no es un requisito, solo para clientes. Cuando mi aplicación Backbone inicia una animación en la que se cambia el desplazamiento, se produce desplazamiento pero no desencadena estas capturas de eventos. Esto se prueba en FF, Safari & Chrome más reciente.

$('html, body').bind('scroll mousedown wheel DOMMouseScroll mousewheel keyup', function(evt) { 
    // detect only user initiated, not by an .animate though 
    if (evt.type === 'DOMMouseScroll' || evt.type === 'keyup' || evt.type === 'mousewheel') { 
    // up 
    if (evt.originalEvent.detail < 0 || (evt.originalEvent.wheelDelta && evt.originalEvent.wheelDelta > 0)) { 
    // down. 
    } else if (evt.originalEvent.detail > 0 || (evt.originalEvent.wheelDelta && evt.originalEvent.wheelDelta < 0)) { 
    } 
} 
}); 
+0

Gracias por la sugerencia. Sin embargo, esta es una solución binaria: detecta el desplazamiento iniciado por JavaScript en comparación con el desplazamiento que no es js. Realmente necesito una solución ternaria. (javascript, usuario, navegador). – mrtsherman

0

si está utilizando JQuery que hay una mejor respuesta, al parecer, solo lo estoy probando yo mismo.

ver: Detect jquery event trigger by user or call by code

+0

Gracias por la sugerencia. Sin embargo, esta es una solución binaria: detecta el desplazamiento iniciado por JavaScript en comparación con el desplazamiento que no es js. Realmente necesito una solución ternaria. (javascript, usuario, navegador). Sidenote, jQuery no es un requisito para su solución propsed. – mrtsherman

0

Con respecto a:

En concreto, cuando se utiliza el botón de retroceso de un navegador puede saltar a la última posición de desplazamiento conocida.

Que se dispara muy pronto, una vez que se muestra la página. Puede demorar la reproducción del evento de desplazamiento en 1 segundo más o menos.

+0

Esta no sería una solución sólida. El tiempo para saltar depende del tiempo de carga de la página. El navegador demora el salto hasta que la página esté completamente cargada. Esto evita que los elementos de página que cargan y cambian la altura de la página (como las imágenes) se deslicen fuera de la vista. – mrtsherman

0

En lugar de tratar de detectar todos los eventos del usuario, es mucho más fácil hacer lo contrario y manejar solo los eventos programáticos, e ignorarlos.

Por ejemplo, este tipo de código funcionaría:

// Element that needs to be scrolled 
var myElement = document.getElementById('my-container'); 

// Flag to tell if the change was programmatic or by the user 
var ignoreNextScrollEvent = false; 

function setScrollTop(scrollTop) { 
    ignoreNextScrollEvent = true; 
    myElement.scrollTop = scrollTop 
} 

myElement.addEventListener('scroll', function() { 
    if (ignoreNextScrollEvent) { 
     // Ignore this event because it was done programmatically 
     ignoreNextScrollEvent = false; 
     return; 
    } 

    // Process user-initiated event here 
}); 

Luego, cuando se llama a setScrollTop(), se ignorará el evento de desplazamiento, mientras que si el usuario desplazarse con el ratón, teclado o cualquier otra forma, el evento será procesado.

Cuestiones relacionadas