2008-11-14 9 views
157

Powiedzmy, że mam aplikację internetową, która ma stronę, która może zawierać 4 bloki skryptów - skrypt, który piszę może znajdować się w jednym z tych bloków, ale nie wiem, który z nich jest obsługiwane przez kontroler.Jak zamówić zdarzenia związane z jQuery

Łączę niektóre zdarzenia onclick z przyciskiem, ale czasami wykonuję je w kolejności, której się nie spodziewałem.

Czy istnieje sposób na zapewnienie porządku lub jak rozwiązałeś ten problem w przeszłości?

Odpowiedz

24

Od dawna próbowałem uogólnić ten proces, ale w moim przypadku dotyczyło to tylko kolejności słuchaczy pierwszego zdarzenia w łańcuchu.

Jeśli to jakiegokolwiek użytku, tutaj jest mój jQuery plugin, który wiąże detektor zdarzeń, który jest zawsze wyzwalany przed wszelkimi innymi:

** UPDATED inline ze zmianami jQuery (dzięki 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); 

warte Uwaga:

  • ten nie został w pełni przetestowany.
  • Opiera się na wewnętrznych elementów jQuery bez zmiany struktury (testowane tylko z 1.5.2).
  • Nie musi zostać wywołane przed detektorami zdarzeń, które są związane w inny sposób niż jako atrybut elementu źródłowego lub przy użyciu funkcji bind() jQuery i innych powiązanych funkcji.
+0

Należy zauważyć, że działa to tylko w przypadku zdarzeń dodanych za pośrednictwem jQuery. – Grinn

+1

to już nie działa, ponieważ używa 'this.data (" events ")', zobacz tutaj http://stackoverflow.com/questions/12214654/jquery-1-8-find-event-handlers – Toskan

+4

Istnieje podobną funkcję, która została zaktualizowana dla jQuery 1.8 tutaj: http: // stackoverflow.com/a/2641047/850782 – EpicVoyage

133

Jeśli zamówienie jest ważne, możesz tworzyć własne zdarzenia i przywracać wywołania zwrotne, gdy te zdarzenia będą uruchamiane przez inne wywołania zwrotne.

$('#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; 
}); 

Coś takiego przynajmniej.

+23

co, jeśli chcemy zatrzymać propagację zdarzenia kliknięcia dla zwrotów zdefiniowanych w pewnym momencie przed naszym kodem powiązania. – vinilios

+2

Czy mogę zapytać, dlaczego nie zostało to zaakceptowane? Aktualnie zaakceptowana odpowiedź cytuje moją odpowiedź jako najlepszą metodę, więc jestem trochę zdezorientowana. :-) – dowski

+0

Działa to w przypadku mkoryaka, ale nie, jeśli to samo zdarzenie jest wywoływane wiele razy. Mam podobny problem, w którym pojedyncze naciśnięcie klawisza uruchamia $ (okno) .scroll() wiele razy, w * odwrotnej * kolejności. – kxsong

15

Kolejność, do której wywoływane są wywołania zwrotne, jest zarządzana przez poszczególne dane zdarzenia obiektu jQuery. Nie ma żadnych funkcji (które znam), które pozwalają na przeglądanie i manipulowanie tymi danymi bezpośrednio, możesz używać tylko bind() i unbind() (lub dowolnej równoważnej funkcji pomocnika).

Metoda Dowskiego jest najlepsza, powinieneś zmodyfikować różne powiązane wywołania zwrotne w celu powiązania z uporządkowaną sekwencją niestandardowych zdarzeń, z "pierwszym" zwrotem związanym z "prawdziwym" zdarzeniem. W ten sposób, bez względu na to, w jakiej kolejności są one powiązane, sekwencja zostanie wykonana we właściwy sposób.

Jedyną alternatywą, którą mogę zobaczyć, jest coś, czego naprawdę, naprawdę nie chcesz kontemplować: jeśli wiesz, że wiążąca składnia funkcji mogła być związana przed tobą, spróbuj odłączyć wszystkie te funkcje, a następnie ponownie zwiąż je we właściwej kolejności. To tylko proszenie o kłopoty, ponieważ teraz masz zduplikowany kod.

Byłoby fajnie, gdyby jQuery pozwolił ci po prostu zmienić kolejność powiązanych zdarzeń w danych zdarzenia obiektu, ale bez napisania jakiegoś kodu do haka do rdzenia jQuery, który wydaje się niemożliwy. I są prawdopodobnie konsekwencje dopuszczenia tego, o czym nie pomyślałem, więc może to celowe zaniedbanie.

+12

, aby zobaczyć wszystkie zdarzenia związane z elementem przez użycie jquery var allEvents = $ .data (this, "events"); – redsquare

3

Można spróbować czegoś takiego: metoda

/** 
    * 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

Myślę, że "właściciele" powinni być po prostu "obsługi" – Joril

+1

To nie będzie działać z nowszą wersją 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

Przepisałem go jako jQuery thingy: https://gist.github.com/infostreams/6540654 – Edward

37

Dowski jest dobra jeśli wszystkie wywołania zwrotne są zawsze będzie obecny i jesteś zadowolony z nich są zależne od siebie.

Jeśli jednak chcesz, aby wywołania zwrotne były od siebie niezależne, możesz wykorzystać zalety bąblowania i dołączyć kolejne zdarzenia jako delegaty do elementów nadrzędnych. Funkcje obsługi elementów nadrzędnych zostaną uruchomione po procedurach obsługi elementu, kontynuując aż do dokumentu. Jest to całkiem niezłe, ponieważ można używać event.stopPropagation(), event.preventDefault(), itp., Aby pominąć procedury obsługi i anulować lub anulować akcję.

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

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

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

Lub, jeśli nie podoba, można użyć Nick wypłukuje wtyczki bindLast wymusić zdarzenie na związanie ostatnia: https://github.com/nickyleach/jQuery.bindLast.

Jeśli używasz jQuery 1.5, możesz również zrobić coś sprytnego z nowym obiektem Odroczony.

+5

'.delegate' nie jest już używany i powinieneś teraz używać '.on', ale nie wiem, jak możesz osiągnąć to samo zachowanie z' on' –

+0

wtyczka musi zostać naprawiona, ponieważ nie działa ona więcej tutaj: http : //stackoverflow.com/questions/12214654/jquery-1-8-find-event-handlers – Toskan

+0

@ eric.itzhak Aby zrobić .on() zachowywać się jak stare .delegate(), po prostu podaj mu selektor. Szczegóły tutaj http://api.jquery.com/delegate/ –

6

tylko wiążą obsługi normalnie, a następnie uruchomić:

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

więc na przykład:

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

Ta praca zapewnia, że ​​wszystkie zdarzenia zostały dodane przez jQuery. –

+0

nie działa i nie ma mowy, że to zadziała –

+1

nie działa, ale to '' '$ ._ data (element, 'zdarzenia') [nazwa] .reverse()' '' działa – Attenzione

8

Należy pamiętać, że we wszechświecie jQuery to musi być realizowane w różny sposób od wersji 1.8. Poniższa uwaga wydanie jest z the jQuery blog:

.data („Wydarzenia”): jQuery przechowuje dane dotyczące zdarzeń w obiekcie danych nazwie (na to czekać) wydarzenia na każdym elemencie. Jest to wewnętrzna struktura danych, więc w wersji 1.8 zostanie ona usunięta z przestrzeni nazw danych użytkownika , dzięki czemu nie będzie kolidować z elementami o tej samej nazwie. dane zdarzenie jQuery nadal można uzyskać za pośrednictwem jQuery._data (element, „wydarzenia”)

Mamy pełną kontrolę nad kolejności, w jakiej koparki wykona we wszechświecie jQuery. Ricoo wskazuje to wyżej. Nie wygląda na to, że jego odpowiedź przyniosła mu wiele miłości, ale ta technika jest bardzo przydatna. Rozważmy, na przykład, za każdym razem trzeba wykonać własną obsługi przed jakimś obsługi w widgecie biblioteki, albo trzeba mieć uprawnienia do odstąpienia od wezwania do obsługi widgetu warunkowo:

$("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

Oto mój strzał na to, obejmujące różne wersje 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 wprowadza obietnic, a oto najprostsza realizacja widziałem kontrolować kolejność wykonywania.Pełna dokumentacja na http://api.jquery.com/jquery.when/

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

W niektórych szczególnych przypadkach, gdy nie można zmienić, jak zdarzenia kliknięcia są związani (powiązania zdarzeń są wykonane z kodami cudzych), można zmienić elementu HTML, tutaj jest możliwe rozwiązanie (ostrzeżenie: to nie zalecany sposób powiązać wydarzenia, inni programiści mogą zamordować za to):

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

w ten sposób wiązania, twój obsługi zdarzeń zostanie dodany pierwszy, więc to będzie najpierw wykonać egzekucję.

+0

To nie jest rozwiązanie, to podpowiedź w rozwiązaniu. Czy sugerujesz, że ten "onclick" obsługuje element, który już posiada program obsługi (jQuery), czy też Twój element * zawiera element obsługi jQuery? – Auspex

1

Mam ten sam problem i znalazłem ten temat. powyższe odpowiedzi mogą rozwiązać ten problem, ale nie sądzę, że są to dobre plany.

pomyślmy o prawdziwym świecie.

jeśli użyjemy tych odpowiedzi, musimy zmienić nasz kod. musisz zmienić swój styl kodu. coś takiego:

oryginalny:

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

Hack:

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

w miarę upływu czasu, pomyśl o swoim projekcie. kod jest brzydki i trudny do odczytania! Antyczny powód jest prosty jest zawsze lepszy. jeśli masz 10 bindAtTheStart, to może nie być żadnych błędów. jeśli masz 100 bindAtTheStart, czy jesteś pewien, że możesz zachować je we właściwej kolejności?

, więc jeśli musisz wiązać te same zdarzenia wiele.Myślę, że najlepszym sposobem jest kontrola js-plik lub kolejność ładowania js-code. jquery może obsługiwać dane zdarzeń jako kolejkę. zamówienie jest pierwsze, pierwsze wychodzi. nie potrzebujesz zmieniać żadnego kodu. po prostu zmień kolejność ładowania.

Powiązane problemy