javascript - Cum să ordonezi evenimentele legate cu jQuery
Să spunem că am o aplicație web care are o pagină ce poate conține 4 blocuri de script - scriptul pe care îl scriu poate fi găsit într-unul dintre aceste blocuri, dar nu știu care, acest lucru este gestionat de controller.
Leag câteva evenimente onclick de un buton, dar constat că uneori acestea se execută într-o ordine la care nu m-am așteptat.
Există vreo modalitate de a asigura ordinea sau cum ați gestionat această problemă în trecut?
Răspuns #1
Dacă ordinea este importantă, poți să creezi propriile evenimente și să atașezi funcții de apel care să fie declanșate atunci când acele evenimente sunt activate de alte funcții de apel.
$('#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;
});
Cel puțin ceva de genul acesta.
Răspunsul #2
Metoda lui Dowski este bună dacă toate callback-urile tale vor fi întotdeauna prezente și ești mulțumit ca ele să fie dependente unele de altele.
Dacă vrei ca apelurile înapoi să fie independente unele de altele, totuși, ai putea să profiți de propagare și să atașezi evenimentele ulterioare ca delegați la elementele părinte. Manipulatoarele de pe un element părinte vor fi declanșate după manipulatoarele de pe element, continuând până la document. Acest lucru este destul de bun deoarece poți folosi event.stopPropagation(), event.preventDefault() etc. pentru a sări peste manipulatoare și a anula sau a nu anula acțiunea.
$( '#mybutton' ).click( function(e) {
// Do stuff first
} );
$( '#mybutton' ).click( function(e) {
// Do other stuff first
} );
$( document ).delegate( '#mybutton', 'click', function(e) {
// Do stuff last
} );
Sau, dacă nu-ți place asta, ai putea folosi pluginul bindLast de la Nick Leach pentru a forța un eveniment să fie legat ultimul: https://github.com/nickyleach/jQuery.bindLast.
Sau, dacă folosești jQuery 1.5, ai putea de asemenea să faci ceva ingenios cu noul obiect Deferred.
Răspunsul #3
Încercam de mult timp să generalizez acest tip de proces, dar în cazul meu eram doar interesat de ordinea primului ascultător de evenimente din lanț.
Dacă este de vreun folos, iată pluginul meu jQuery care atașează un ascultător de evenimente care este întotdeauna declanșat înaintea oricăruia altuia:
** ACTUALIZAT conform modificărilor jQuery (mulțumiri 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);
Lucruri de remarcat:
- Acest lucru nu a fost testat complet.
- Se bazează pe faptul că elementele interne ale framework-ului jQuery nu se schimbă (testat doar cu 1.5.2).
- Nu va fi neapărat declanșat înaintea ascultătorilor de evenimente care sunt legați în orice mod altul decât ca atribut al elementului sursă sau folosind bind() de la jQuery și alte funcții asociate.
Răspunsul #4
Ordinea în care sunt apelate callback-urile legate este gestionată de datele de eveniment ale fiecărui obiect jQuery. Nu există funcții (cât știu eu) care să vă permită să vizualizați și să manipulați direct aceste date, puteți folosi doar bind() și unbind() (sau oricare dintre funcțiile ajutătoare echivalente).
Metoda lui Dowski este cea mai bună, ar trebui să modifici diversele callback-uri legate pentru a se atașa la o secvență ordonată de evenimente personalizate, cu callback-ul „primul” legat de evenimentul „real”. În acest fel, indiferent în ce ordine sunt legate, secvența se va executa corect.
Singura alternativă pe care o pot vedea este ceva ce chiar, chiar nu vrei să contemplăți: dacă știi că sintaxa de legare a funcțiilor ar fi putut fi legată înaintea ta, încearcă să de-legi toate acele funcții și apoi să le re-ligi în ordinea corectă tu însuți. Asta este pur și simplu să ceri probleme, pentru că acum ai cod duplicat.
Ar fi grozav dacă jQuery ar permite pur și simplu să schimbi ordinea evenimentelor atașate în datele de eveniment ale unui obiect, dar fără să scrii un cod pentru a te lega de nucleul jQuery, acest lucru nu pare posibil. Și probabil există implicații ale permiterii acestui lucru la care nu m-am gândit, așa că poate este o omisiune intenționată.
Răspunsul #5
Rețineți că în universul jQuery acest lucru trebuie implementat diferit începând cu versiunea 1.8. Următoarea notă de lansare provine de pe blogul jQuery:
.data(“events”): jQuery stochează datele sale legate de evenimente într-un obiect de date numit (așteptați puțin) events pe fiecare element. Aceasta este o structură de date internă, așa că în versiunea 1.8 aceasta va fi eliminată din spațiul de nume al datelor utilizatorului pentru a nu intra în conflict cu elemente cu același nume. Datele despre evenimentele jQuery pot fi încă accesate prin jQuery._data(element, "events")
Avem într-adevăr control complet asupra ordinii în care vor executa handlerii în universul jQuery. Ricoo subliniază acest lucru mai sus. Nu pare că răspunsul său i-a adus multă apreciere, dar această tehnică este foarte utilă. Luați, de exemplu, orice situație în care aveți nevoie să executați propriul handler înainte de un handler dintr-un widget al unei biblioteci sau aveți nevoie de puterea de a anula apelul către handlerul widgetului condiționat:
$("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();
});
