2011-11-08 12 views
27

Estamos trabajando en una herramienta JavaScript que tiene un código anterior, , por lo que no podemos volver a escribir la herramienta completa.¿Cómo manejo un clic en algún lugar de la página, incluso cuando un elemento determinado detiene la propagación?

Ahora, se agregó un menú de posición fija en la parte inferior y al cliente le gustaría tener un botón para abrir y cerrar el menú, , excepto que el cierre debe realizarse automáticamente cuando un usuario comienza a hacer cosas del menú, por ejemplo, cuando un usuario vuelve a la página y selecciona algo o hace clic en un campo de formulario.

Esto podría funcionar técnicamente con un evento click en el body, lo que provocó en cualquier clic, sin embargo hay numerosos artículos en el código antiguo, donde un evento de clic se manejó en un enlace interno, y return false se añadió al clic función, para que el sitio no continúe con la etiqueta href del enlace.

Así que, claramente, una función general como esta funciona, pero no cuando se hace clic en un enlace interno donde el retorno falso detiene la propagación.

$('body').click(function(){ 
    console.log('clicked'); 
}); 

¿Hay alguna manera de forzar el acontecimiento de cuerpo clic en cualquier caso, o hay otra manera de que pueda dejar que el dissappear menú, utilizando algún evento click global o algo similar?

Sin tener que volver a escribir todos los demás clics en la aplicación que se crearon hace años. Eso sería una tarea monstruosa, especialmente porque no tengo ni idea de cómo los reescribiría, sin la devolución falsa, pero aún así no los dejo ir a su href.

Respuesta

44

Eventos en implementaciones DOM modernos tienen dos fases, captura y burbujeante.La fase de captura es la primera fase, que fluye del defaultView del documento al objetivo del evento, seguida de la fase de burbujeo, que fluye desde el objetivo del evento al defaultView. Para obtener más información, consulte http://www.w3.org/TR/DOM-Level-3-Events/#event-flow.

para manejar la fase de la captura de un evento, es necesario establecer el tercer argumento para addEventListener a true:

document.body.addEventListener('click', fn, true); 

Lamentablemente, como se ha mencionado Wesley, la fase de captura de un evento no puede ser manejado de forma fiable, o en absoluto, en navegadores más antiguos.

Una posible solución es para controlar el evento mouseup lugar, puesto que el orden de eventos para clics es:

  1. mousedown
  2. mouseup
  3. clic

Si usted puede estar seguro de que tiene no hay controladores que cancelen el evento mouseup, entonces esta es una forma (y, posiblemente, una mejor manera) de ir. Otra cosa a tener en cuenta es que muchos, si no la mayoría (si no todos), los menús de la interfaz de usuario desaparecen con el mouse hacia abajo.

+2

Para cualquiera que se pregunte si puede usar esto, aquí está la página de caniuse: http://caniuse.com/#feat=addeventlistener la respuesta corta es sí, siempre y cuando no le importe IE8 –

0

creo que esto es lo que necesita:

$("body").trigger("click"); 

Esto le permitirá para lanzar el evento cuerpo clic desde cualquier lugar.

+0

¿Eso disparará si algo anidado más profundo en el dom evita la propagación del evento? – Matt

+0

Creo que el problema es que los eventos de clic se están tragando al devolver false de los controladores de eventos en ciertos enlaces, no es que el evento de clic no se pudo generar en primer lugar. –

0

Si se asegura de que este es el primer trabajo manejador caso, algo como esto podría hacer el truco:

$('*').click(function(event) { 
    if (this === event.target) { // only fire this handler on the original element 
     alert('clicked'); 
    } 
}); 

Tenga en cuenta que, si tiene muchos elementos en su página, esto será muy lento, y no funcionará para nada agregado dinámicamente.

+0

la cosa dinámica podría arreglarse fácilmente a través de $ ("*"). On ("click", function() {}); pero a pesar de esto, es una mala idea. – Bbit

6

En cooperación con Andy E, este es el lado oscuro de la fuerza:

var _old = jQuery.Event.prototype.stopPropagation; 

jQuery.Event.prototype.stopPropagation = function() { 
    this.target.nodeName !== 'SPAN' && _old.apply(this, arguments); 
};  

Ejemplo: http://jsfiddle.net/M4teA/2/

Recuerde, si todos los eventos fueron atados a través de jQuery, que pueda manejar esos casos solo aquí. En este ejemplo, simplemente llamamos al .stopPropagation() original si no estamos tratando con un <span>.


No puede evitar la prevención, no.

Lo que podría hacer es reescribir esos controladores de eventos manualmente en el código. Esto es un asunto complicado, pero si sabe cómo acceder a los métodos del controlador almacenado, podría evitarlo. Jugué un rato con él un poco, y este es mi resultado:

$(document.body).click(function() { 
    alert('Hi I am bound to the body!'); 
}); 

$('#bar').click(function(e) { 
    alert('I am the span and I do prevent propagation'); 
    e.stopPropagation(); 
}); 

$('#yay').click(function() { 
    $('span').each(function(i, elem) { 
     var events  = jQuery._data(elem).events, 
      oldHandler = [ ], 
      $elem   = $(elem); 

     if('click' in events) {       
      [].forEach.call(events.click, function(click) { 
       oldHandler.push(click.handler); 
      }); 

      $elem.off('click'); 
     } 

     if(oldHandler.length) { 
      oldHandler.forEach(function(handler) { 
       $elem.bind('click', (function(h) { 
        return function() { 
         h.apply(this, [{stopPropagation: $.noop}]); 
        }; 
       }(handler))); 
      }); 
     } 
    }); 

    this.disabled = 1; 
    return false; 
}); 

Ejemplo: http://jsfiddle.net/M4teA/

Aviso, el código anterior sólo funcionará con jQuery 1.7. Si esos eventos de clic se vincularon con una versión anterior de jQuery o "en línea", aún puede usar el código pero necesitaría acceder al "controlador anterior" de manera diferente.

Sé que estoy asumiendo muchas cosas del escenario del "mundo perfecto" aquí, por ejemplo, que esos identificadores explícitamente llaman a .stopPropagation() en lugar de devolver false. Por lo que todavía podría ser un ejemplo académico inútil, pero sentí salir con él :-)

edición: hey, return false; funcionará bien, los objetos de eventos se accede de la misma manera.

+0

Esto es inteligente. :) –

Cuestiones relacionadas