JQuery ile ilişkili olaylar nasıl sipariş edilir


164

4 komut dosyası bloğu içerebilecek bir sayfaya sahip bir web uygulamam olduğunu varsayalım - yazdığım komut dosyası bu bloklardan birinde bulunabilir, ancak hangisinin denetleyici tarafından işlendiğini bilmiyorum.

Bazı onclickolayları bir düğmeye bağlarım, ancak bazen beklemediğim bir sırada yürüttüklerini görüyorum.

Düzeni sağlamanın bir yolu var mı veya geçmişte bu sorunu nasıl ele aldınız?


1
Geri aramalarınızı bir CallBack nesnesine ekleyin ve onları ateşlemek istediğinizde hepsini bir kerede ateşleyebilirsiniz ve eklenen sırayla çalışırlar. api.jquery.com/jQuery.Callbacks
Tyddlywink

Yanıtlar:


28

Yaşları boyunca bu tür bir süreci genelleştirmeye çalışıyordum, ancak benim durumumda sadece zincirdeki ilk olay dinleyicisinin sırası ile ilgileniyordum.

Herhangi bir kullanım varsa, her zaman diğerlerinden önce tetiklenen bir olay dinleyicisini bağlayan jQuery eklentim:

** jQuery değişiklikleriyle GÜNCELLEŞTİRİLMİŞ (teşekkürler 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);

Dikkat edilmesi gerekenler:

  • Bu tam olarak test edilmedi.
  • Değişmeyen jQuery çerçevesinin iç kısımlarına dayanır (sadece 1.5.2 ile test edilmiştir).
  • Kaynak öğenin özniteliği veya jQuery bind () ve diğer ilişkili işlevleri kullanmak dışında herhangi bir şekilde bağlanan olay dinleyicilerinden önce tetiklenmesi gerekmez.

Bunun yalnızca jQuery aracılığıyla eklenen etkinlikler için de işe yaradığını unutmayın.
Grinn

1
bu, kullanıldığından beri artık işe yaramıyor this.data("events"), buraya bakın stackoverflow.com/questions/12214654/…
Toskan

4
Burada jQuery 1.8 için güncellenmiş benzer bir işlev var: stackoverflow.com/a/2641047/850782
EpicVoyage

136

Sipariş önemliyse, kendi etkinliklerinizi oluşturabilir ve bu etkinlikler diğer geri aramalar tarafından tetiklendiğinde geri çağrıları tetikleyebilirsiniz.

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

En azından böyle bir şey.


23
bağlama kodumuzdan önceki bir noktada tanımlanan geri aramalar için tıklama etkinliğinin yayılmasını durdurmak istiyorsak.
vinilios

2
Bunun neden kabul edilmediğini sorabilir miyim? Şu anda kabul edilen cevap cevabımı en iyi yöntem olarak belirtiyor, bu yüzden biraz kafam karıştı. :-)
dowski

Bu mkoryak'ın davası için geçerlidir, ancak aynı olay birden fazla kez çağrılıyorsa değil. Tek bir tuş vuruşu $ (pencere) .scroll () birden çok kez ters sırayla tetiklediği benzer bir sorun var .
kxsong

37

Dowski'nin yöntemi, tüm geri aramalarınız her zaman mevcut olacaksa ve bunların birbirine bağımlı olmasından memnunsanız.

Bununla birlikte, geri çağrıların birbirinden bağımsız olmasını istiyorsanız, köpürmekten yararlanabilir ve sonraki olayları üst öğelere delege olarak ekleyebilirsiniz. Bir üst öğedeki işleyiciler, öğedeki işleyicilerin ardından belgeye kadar devam edecek şekilde tetiklenir. Bu kullanabilirsiniz olarak oldukça iyidir event.stopPropagation(), event.preventDefault()vb işleyicileri atlayıp iptal veya işlemle ilgili iptal etmek.

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

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

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

Ya da bunu beğenmezseniz, bir olayın en son bağlanmasını sağlamak için Nick Leaches bindLast eklentisini kullanabilirsiniz: https://github.com/nickyleach/jQuery.bindLast .

Veya jQuery 1.5 kullanıyorsanız, potansiyel olarak yeni Ertelenmiş nesne ile akıllıca bir şeyler yapabilirsiniz.


6
.delegateartık kullanılmıyor ve şimdi kullanmalısınız .on, ancak aynı davranışa nasıl ulaşacağınızı bilmiyorumon
eric.itzhak

eklentinin artık çalışmadığı için düzeltilmesi gerekiyor, buraya bakın: stackoverflow.com/questions/12214654/…
Toskan

@ eric.itzhak .on () 'un eski .delegate () gibi davranmasını sağlamak için ona bir seçici sağlayın. Ayrıntılar burada api.jquery.com/delegate
Sam Kauffman

Köpürmenin avantajından yararlanmak istiyorsanız, etkinliğin yetki vermek yerine doğrudan olması gerektiğini düşünüyorum . api.jquery.com/on/#direct-and-delegated-events
Ryne Everett

15

İlişkili geri çağrıların çağrılma sırası her jQuery nesnesinin olay verileri tarafından yönetilir. Bu verileri doğrudan görüntülemenize ve değiştirmenize izin veren herhangi bir işlev (bildiğim) yoktur, yalnızca bind () ve unbind () (veya eşdeğer yardımcı işlevlerden herhangi birini) kullanabilirsiniz.

Dowski'nin yöntemi en iyisidir, sıralı bir özel etkinlik sırasına bağlanmak için çeşitli ilişkili geri çağrıları değiştirmelisiniz, "ilk" geri çağırma "gerçek" olayına bağlıdır. Bu şekilde, hangi sırada bağlandıklarına bakılmaksızın, sıra doğru şekilde yürütülür.

Görebildiğim tek alternatif, gerçekten düşünmek istemediğiniz bir şeydir: işlevlerin bağlayıcı sözdiziminin sizden önce bağlı olabileceğini biliyorsanız, tüm bu işlevleri kaldırmayı ve sonra yeniden bağlamayı deneyin kendiniz uygun sırayla. Bu sadece sorun istiyor, çünkü şimdi çoğaltılmış kodunuz var.

JQuery, bir nesnenin olay verilerindeki ilişkili olayların sırasını değiştirmenize izin verdiyse, ancak jQuery çekirdeğine kanca yapmak için bazı kod yazmadan izin vermeniz harika olurdu. Muhtemelen düşünmediğim buna izin vermenin etkileri de olabilir, bu yüzden belki kasıtlı bir ihmaldir.


12
jquery ile bir öğeye bağlı tüm olayları görmek için var allEvents = $ .data (this, "events");
08:34

9

JQuery evreninde bunun 1.8 sürümünden farklı bir şekilde uygulanması gerektiğini lütfen unutmayın. Aşağıdaki sürüm notu jQuery blogundandır:

.data (“events”): jQuery, olayla ilgili verilerini her öğedeki (bekle) olaylar adlı bir veri nesnesinde depolar. Bu bir iç veri yapısıdır, bu nedenle 1.8'de bu, kullanıcı veri adı alanından kaldırılır, böylece aynı addaki öğelerle çakışmaz. jQuery'nin etkinlik verilerine hala jQuery._data (öğe, "etkinlikler") üzerinden erişilebilir

İşleyicilerin jQuery evreninde hangi sırayla yürütülecekleri üzerinde tam kontrole sahibiz . Ricoo bunu yukarıda belirtiyor. Cevabı ona çok fazla sevgi kazandırmış gibi görünmüyor, ancak bu teknik çok kullanışlı. Örneğin, bir kitaplık widget'ındaki bir işleyiciden önce kendi işleyicinizi yürütmeniz gerektiğinde veya widget'ın işleyicisine yapılan çağrıyı koşullu olarak iptal etme gücüne sahip olmanız gerektiğinde düşünün:

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

7

sadece işleyiciyi normal bir şekilde bağlayın ve çalıştırın:

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

Yani mesela:

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

2
çalışmıyor, ama $._data(element, 'events')[name].reverse()çalışıyor
Attenzione

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;
}

3

Böyle bir şey deneyebilirsiniz:

/**
  * 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]);
    }   
}

Bence "ownerhandlers" sadece "handlers" olmalı
Joril

1

Aynı sorunu yaşadım ve bu konuyu buldum. Yukarıdaki yanıtlar bu sorunu çözebilir, ancak bunların iyi planlar olduğunu düşünmüyorum.

gerçek dünyayı düşünelim.

bu cevapları kullanırsak, kodumuzu değiştirmeliyiz. kod stilinizi değiştirmeniz gerekir. böyle bir şey:

orijinal:

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

hile:

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

zaman geçtikçe projenizi düşünün. kod çirkin ve okunması zor! antife sebebi basit her zaman iyidir. 10 bindAtTheStart varsa, hata olmayabilir. 100 bindAtTheStart'ınız varsa, bunları doğru sırada tutabileceğinizden gerçekten emin misiniz?

Aynı olayları birden fazla bağlamak zorundaysanız, bence en iyi yol kontrol js dosyası veya js kodu yükleme sırasıdır. jquery olay verilerini kuyruk olarak işleyebilir. sipariş ilk giren ilk çıkar. herhangi bir kodu değiştirmenize gerek yoktur. sadece yük sırasını değiştirin.


0

İşte benim jQuery farklı sürümlerini kapsayan benim çekim:

// 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;
            }
        });
    }
});

0

Bazı özel durumlarda, tıklama etkinliklerinin nasıl bağlandığını değiştiremiyorsanız (etkinlik bağlamaları başkalarının kodlarından yapılır) ve HTML öğesini değiştirebildiğinizde, olası bir çözüm ( uyarı : bu, bağlamanın önerilen yolu değildir etkinlikler, diğer geliştiriciler sizi bunun için öldürebilir):

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

Bu ciltleme yöntemiyle, önce olay işleyiciniz eklenir, böylece önce yürütülür.


Bu bir çözüm değil, bir çözümün ipucu. Bu "onclick" işleyicisi zaten (jQuery) işleyici var ya da <span> olduğunu elemanı oluyor söylüyorsunuz sarma jQuery işleyicisi ile eleman?
Auspex

-2

JQuery 1.5 vaat ediyor ve yürütme sırasını kontrol etmek için gördüğüm en basit uygulama. Http://api.jquery.com/jquery.when/ adresindeki tüm belgeler

$.when( $('#myDiv').css('background-color', 'red') )
 .then( alert('hi!') )
 .then( myClickFunction( $('#myID') ) )
 .then( myThingToRunAfterClick() );
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.