2008-11-14 27 views
157

Digamos que tengo una aplicación web que tiene una página que puede contener 4 bloques de script: el script que escribo se puede encontrar en uno de esos bloques, pero no sé cuál, ese es manejado por el controlador.Cómo solicitar eventos vinculados con jQuery

Ato algunos eventos onclick a un botón, pero me parece que a veces se ejecutan en un orden que no esperaba.

¿Hay alguna manera de garantizar el orden o cómo ha manejado este problema en el pasado?

Respuesta

24

He intentado durante años para generalizar este tipo de proceso, pero en mi caso solo me preocupaba el orden del primer oyente del evento en la cadena.

Si es de alguna utilidad, aquí es mi plugin de jQuery que se une a un detector de eventos que siempre se activa antes que a otros:

** ACTUALIZADO en línea con los cambios de jQuery (gracias Toskan) **

(function($) { 
    $.fn.bindFirst = function(/*String*/ eventType, /*[Object])*/ eventData, /*Function*/ handler) { 
     var indexOfDot = eventType.indexOf("."); 
     var eventNameSpace = indexOfDot > 0 ? eventType.substring(indexOfDot) : ""; 

     eventType = indexOfDot > 0 ? eventType.substring(0, indexOfDot) : eventType; 
     handler = handler == undefined ? eventData : handler; 
     eventData = typeof eventData == "function" ? {} : eventData; 

     return this.each(function() { 
      var $this = $(this); 
      var currentAttrListener = this["on" + eventType]; 

      if (currentAttrListener) { 
       $this.bind(eventType, function(e) { 
        return currentAttrListener(e.originalEvent); 
       }); 

       this["on" + eventType] = null; 
      } 

      $this.bind(eventType + eventNameSpace, eventData, handler); 

      var allEvents = $this.data("events") || $._data($this[0], "events"); 
      var typeEvents = allEvents[eventType]; 
      var newEvent = typeEvents.pop(); 
      typeEvents.unshift(newEvent); 
     }); 
    }; 
})(jQuery); 

a tener en cuenta:

  • Esto no se ha probado completamente.
  • Se basa en que las partes internas de la infraestructura jQuery no cambian (solo se ha probado con 1.5.2).
  • No necesariamente se activará antes de los detectores de eventos que están vinculados de cualquier manera que no sea como un atributo del elemento de origen o utilizando jQuery bind() y otras funciones asociadas.
+0

Es importante tener en cuenta que esto también solo funciona para eventos que se agregaron a través de jQuery. – Grinn

+1

esto no funciona más ya que usa 'this.data (" events ")', mira aquí http://stackoverflow.com/questions/12214654/jquery-1-8-find-event-handlers – Toskan

+4

Hay un función similar que se ha actualizado para jQuery 1.8 aquí: http: // stackoverflow.com/a/2641047/850782 – EpicVoyage

133

Si el orden es importante, puede crear sus propios eventos y vincular devoluciones de llamadas para que se activen cuando esos eventos se desencadenen por otras devoluciones de llamada.

$('#mydiv').click(function(e) { 
    // maniplate #mydiv ... 
    $('#mydiv').trigger('mydiv-manipulated'); 
}); 

$('#mydiv').bind('mydiv-manipulated', function(e) { 
    // do more stuff now that #mydiv has been manipulated 
    return; 
}); 

Algo así como eso.

+23

¿Qué sucede si queremos detener la propagación del evento de clic para las devoluciones de llamada definidas en algún momento antes de nuestro código de vinculación? – vinilios

+2

¿Puedo preguntar por qué esto no fue aceptado? La respuesta actualmente aceptada cita mi respuesta como el mejor método, así que estoy un poco confundido. :-) – dowski

+0

Esto funciona para el caso de mkoryak, pero no si el mismo evento se llama varias veces. Tengo un problema similar, donde una sola pulsación de tecla desencadena $ (ventana) .scroll() varias veces, en orden * inverso *. – kxsong

15

El orden en que se invocan las devoluciones de llamada encuadernadas es gestionado por los datos de evento de cada objeto de jQuery. No hay funciones (que yo sepa) que le permitan ver y manipular esos datos directamente, solo puede usar bind() y desvincular() (o cualquiera de las funciones auxiliares equivalentes).

El método de Dowski es el mejor, debe modificar las diversas devoluciones de llamada enlazadas para enlazar a una secuencia ordenada de eventos personalizados, con la "primera" devolución de llamada vinculada al evento "real". De esa forma, sin importar en qué orden estén vinculados, la secuencia se ejecutará de la manera correcta.

La única alternativa que puedo ver es algo que realmente, realmente, no quiero contemplar: si sabe que la sintaxis vinculante de las funciones puede haber sido atada antes que usted, intente desconectar todas esas funciones y luego vuelva a atarlos en el orden correcto usted mismo. Eso es solo pedir problemas, porque ahora tienes código duplicado.

Sería genial si jQuery le permitiera simplemente cambiar el orden de los eventos enlazados en los datos de eventos de un objeto, pero sin escribir algún código para enganchar en el núcleo de jQuery que no parece posible. Y probablemente haya implicaciones de permitir esto que no he pensado, así que tal vez sea una omisión intencional.

+12

para ver todos los eventos vinculados a un elemento mediante el uso de jquery var allEvents = $ .data (this, "events"); – redsquare

3

Usted puede intentar algo como esto: Método

/** 
    * Guarantee that a event handler allways be the last to execute 
    * @param owner The jquery object with any others events handlers $(selector) 
    * @param event The event descriptor like 'click' 
    * @param handler The event handler to be executed allways at the end. 
**/ 
function bindAtTheEnd(owner,event,handler){ 
    var aux=function(){owner.unbind(event,handler);owner.bind(event,handler);}; 
    bindAtTheStart(owner,event,aux,true); 

} 
/** 
    * Bind a event handler at the start of all others events handlers. 
    * @param owner Jquery object with any others events handlers $(selector); 
    * @param event The event descriptor for example 'click'; 
    * @param handler The event handler to bind at the start. 
    * @param one If the function only be executed once. 
**/ 
function bindAtTheStart(owner,event,handler,one){ 
    var eventos,index; 
    var handlers=new Array(); 
    owner.unbind(event,handler); 
    eventos=owner.data("events")[event]; 
    for(index=0;index<eventos.length;index+=1){ 
     handlers[index]=eventos[index]; 
    } 
    owner.unbind(event); 
    if(one){ 
     owner.one(event,handler); 
    } 
    else{ 
     owner.bind(event,handler); 
    } 
    for(index=0;index<handlers.length;index+=1){ 
     owner.bind(event,ownerhandlers[index]); 
    } 
} 
+0

Creo que "ownerhandlers" debería ser solo "handlers" – Joril

+1

Esto ya no funcionará con la versión más reciente de jQuery –

7
function bindFirst(owner, event, handler) { 
    owner.unbind(event, handler); 
    owner.bind(event, handler); 

    var events = owner.data('events')[event]; 
    events.unshift(events.pop()); 

    owner.data('events')[event] = events; 
} 
+0

Lo reescribí como una cosa de jQuery: https://gist.github.com/infostreams/6540654 – Edward

37

de Dowski es bueno si todos sus devoluciones de llamada siempre van a estar presentes y que están contentos con ellos siendo dependientes entre sí.

Si desea que las devoluciones de llamadas sean independientes entre sí, puede aprovechar el burbujeo y adjuntar eventos posteriores como delegados a elementos principales. Los controladores en los elementos principales se activarán después de los manejadores en el elemento, continuando hasta el documento. Esto es bastante bueno ya que puede usar event.stopPropagation(), event.preventDefault(), etc. para omitir los manejadores y cancelar o cancelar la acción.

$('#mybutton').click(function(e) { 
    // Do stuff first 
}); 

$('#mybutton').click(function(e) { 
    // Do other stuff first 
}); 

$(document).delegate('#mybutton', 'click', function(e) { 
    // Do stuff last 
}); 

O, si no te gusta esto, se podía utilizar Nick se filtra bindLast plugin para forzar un evento en obligarse última: https://github.com/nickyleach/jQuery.bindLast.

O, si está utilizando jQuery 1.5, también podría hacer algo inteligente con el nuevo objeto aplazado.

+5

'.delegate' ya no se usa y ahora debes usar '.on', pero no sé cómo puedes alcanzar el mismo comportamiento con' on' –

+0

el complemento debe ser reparado ya que no funciona más, mira aquí: http : //stackoverflow.com/questions/12214654/jquery-1-8-find-event-handlers – Toskan

+0

@ eric.itzhak Para hacer que .on() se comporte como el antiguo .delegate(), simplemente proporcione un selector. Detalles aquí http://api.jquery.com/delegate/ –

6

manejador simplemente se unen normalmente y luego ejecute:

element.data('events').action.reverse(); 

así por ejemplo:

$('#mydiv').data('events').click.reverse(); 
+4

Esto funciona siempre que todos los eventos hayan sido agregados por jQuery. –

+0

no funciona y ic de ninguna manera funcionaría –

+1

no funciona, pero este '' '$ ._ data (element, 'events') [name] .reverse()' '' funciona – Attenzione

8

Tenga en cuenta que en el universo jQuery esto debe ser implementado de manera diferente a partir de la versión 1.8. La siguiente nota de lanzamiento es de the jQuery blog:

.data (“eventos”): jQuery almacena sus datos relacionados con eventos en un objeto de datos llamado (esperar a que) los eventos en cada elemento. Esta es una estructura interna de datos , por lo que en 1.8 esto se eliminará del espacio de nombre de datos de usuario para que no entre en conflicto con elementos del mismo nombre. datos de eventos de jQuery todavía se puede acceder a través de jQuery._data (elemento, "eventos")

Tenemos el control total de la orden en el que los manipuladores se ejecutarán en el universo jQuery. Ricoo señala esto arriba. No parece que su respuesta le haya ganado mucho amor, pero esta técnica es muy útil. Consideremos, por ejemplo, cada vez que necesita para ejecutar su propio controlador antes de que algún manejador en un widget de la biblioteca, o es necesario tener el poder para cancelar la llamada al manejador del widget de forma condicional:

$("button").click(function(e){ 
    if(bSomeConditional) 
     e.stopImmediatePropagation();//Don't execute the widget's handler 
}).each(function() { 
    var aClickListeners = $._data(this, "events").click; 
    aClickListeners.reverse(); 
}); 
0

Aquí es mi tiro en este, cubriendo diferentes versiones de jQuery:

// Binds a jQuery event to elements at the start of the event chain for that type. 
jQuery.extend({ 
    _bindEventHandlerAtStart: function ($elements, eventType, handler) { 
     var _data; 

     $elements.bind(eventType, handler); 
     // This bound the event, naturally, at the end of the event chain. We 
     // need it at the start. 

     if (typeof jQuery._data === 'function') { 
      // Since jQuery 1.8.1, it seems, that the events object isn't 
      // available through the public API `.data` method. 
      // Using `$._data, where it exists, seems to work. 
      _data = true; 
     } 

     $elements.each(function (index, element) { 
      var events; 

      if (_data) { 
       events = jQuery._data(element, 'events')[eventType]; 
      } else { 
       events = jQuery(element).data('events')[eventType]; 
      } 

      events.unshift(events.pop()); 

      if (_data) { 
       jQuery._data(element, 'events')[eventType] = events; 
      } else { 
       jQuery(element).data('events')[eventType] = events; 
      } 
     }); 
    } 
}); 
-2

jQuery 1.5 introduce promesas, y aquí es la aplicación más sencilla que he visto para controlar el orden de ejecución.La documentación completa en http://api.jquery.com/jquery.when/

$.when($('#myDiv').css('background-color', 'red')) 
.then(alert('hi!')) 
.then(myClickFunction($('#myID'))) 
.then(myThingToRunAfterClick()); 
0

En algunos casos especiales, cuando no se puede cambiar la forma están ligados a los eventos de clic (enlaces de eventos se hacen de los códigos de otros), y se puede cambiar el elemento HTML, que aquí hay una posible solución (advertencia: esto es no la forma recomendada para enlazar eventos, otros desarrolladores pueden matarte por esto):

<span onclick="yourEventHandler(event)">Button</span> 

con esta forma de unión, el lanzador evento se añade en primer lugar, por lo que se ser ejecutado primero.

+0

Eso no es una solución, es una pista en una solución. ¿Está sugiriendo que este manejador "onclick" vaya al elemento que ya tiene un manejador (jQuery), o es su * envolviendo * el elemento con el manejador jQuery? – Auspex

1

Tengo el mismo problema y encontré este tema. las respuestas anteriores pueden resolver esos problemas, pero no creo que sean buenos planes.

pensemos en el mundo real.

si usamos esas respuestas, tenemos que cambiar nuestro código. tienes que cambiar tu estilo de código algo como esto:

el original:

$('form').submit(handle); 

Hack:

bindAtTheStart($('form'),'submit',handle); 

medida que pasa el tiempo, piense en su proyecto. ¡el código es feo y difícil de leer! la razón de anthoer es simple es siempre mejor. si tiene 10 bindAtTheStart, es posible que no tenga errores. si tiene 100 bindAtTheStart, ¿está seguro de poder mantenerlos en el orden correcto?

así que si tiene que vincular los mismos eventos múltiples.Creo que la mejor manera es controlar el orden de carga js-file o js-code. jquery puede manejar datos de eventos como cola. el orden es primero en entrar, primero en salir. no es necesario cambiar ningún código. solo cambia el orden de carga.

Cuestiones relacionadas