Tüm jquery olayları $ (belge) ile ilişkili mi?


164

Bunun nereden geldiği

Ben jQuery ilk öğrendiğimde, normalde böyle olaylar ekledi:

$('.my-widget a').click(function() {
    $(this).toggleClass('active');
});

Seçici hızı ve etkinlik yetkisi hakkında daha fazla bilgi edindikten sonra, birçok yerde "jQuery etkinlik yetkisinin kodunuzu daha hızlı hale getireceğini" okudum. Bu yüzden böyle bir kod yazmaya başladım:

$('.my-widget').on('click','a',function() {
    $(this).toggleClass('active');
});

Bu, kullanımdan kaldırılmış .live () olayının davranışını çoğaltmanın önerilen yoluydu. Sitelerimin çoğu dinamik olarak her zaman widget ekliyor / kaldırıyor. Yukarıdakiler tam olarak .live () gibi davranmaz, çünkü sadece zaten var olan '.my-widget' kapsayıcısına eklenen öğeler davranışı alır. Bu kod çalıştırıldıktan sonra dinamik olarak başka bir html bloğu eklersem, bu öğeler kendilerine bağlı olayları almaz. Bunun gibi:

setTimeout(function() {
    $('body').append('<div class="my-widget"><a>Click does nothing</a></div>');
}, 1000);


Ne elde etmek istiyorum:

  1. .live () // 'nin eski davranışı, henüz varolmayan öğelere olay ekleme
  2. .on () işlevinin faydaları
  3. olayları bağlamak için en hızlı performans
  4. Etkinlikleri yönetmenin basit yolu

Şimdi böyle tüm olayları ekliyorum:

$(document).on('click.my-widget-namespace', '.my-widget a', function() {
    $(this).toggleClass('active');
});

Bu da tüm hedeflerimi karşılıyor gibi görünüyor. (Evet, bir nedenden ötürü IE'de daha yavaş, neden olduğu hakkında bir fikrim yok?) Hızlı çünkü tek bir öğeye yalnızca tek bir olay bağlı ve ikincil seçici yalnızca olay gerçekleştiğinde değerlendiriliyor (lütfen burada yanlış varsa beni düzeltin). Ad alanı harika çünkü olay dinleyicisini değiştirmeyi kolaylaştırıyor.

Çözümüm / Sorum

Bu yüzden jQuery olaylarının her zaman $ (belge) ile bağlanması gerektiğini düşünmeye başladım.
Bunu yapmak istememenizin bir nedeni var mı?
Bu en iyi uygulama olarak düşünülebilir mi? Değilse, neden?

Bütün bunları okuduysanız, teşekkür ederim. Herhangi bir geri bildirim / öngörü için teşekkür ederim.

Varsayımlar:

  1. .on()// en az sürüm 1.7'yi destekleyen jQuery kullanma
  2. Etkinliğin dinamik olarak eklenen içeriğe eklenmesini istiyorsunuz

Kaynaklar / Örnekleri:

  1. http://24ways.org/2011/your-jquery-now-with-less-suck
  2. http://brandonaaron.net/blog/2010/03/4/event-delegation-with-jquery
  3. http://www.jasonbuckboyer.com/playground/speed/speed.html
  4. http://api.jquery.com/on/

Yanıtlar:


215

Hayır - yetki verilen tüm olay işleyicilerini documentnesneye BAĞLAMAMALISINIZ. Bu muhtemelen oluşturabileceğiniz en kötü performans senaryosudur.

Öncelikle, etkinlik devri kodunuzu her zaman daha hızlı hale getirmez. Bazı durumlarda, avantajlıdır ve bazı durumlarda değildir. Etkinlik yetkilendirmesine gerçekten ihtiyaç duyduğunuzda ve bundan yararlanabiliyorsanız etkinlik yetkisi kullanmalısınız. Aksi takdirde, genellikle daha verimli olacağından, olay işleyicilerini doğrudan olayın gerçekleştiği nesnelere bağlamalısınız.

İkinci olarak, temsil edilen tüm olayları belge düzeyinde BAĞLAMAMALISINIZ. İşte tam da bu nedenle .live()itiraz edildi, çünkü bu şekilde çok sayıda olayınız olduğunda bu çok verimsiz. Yetki verilen olay işleme için, bunları dinamik olmayan en yakın ebeveyne bağlamak çok daha verimlidir.

Üçüncü olarak, tüm olaylar işe yaramaz veya tüm sorunlar yetki devri ile çözülemez. Örneğin, bir giriş denetimindeki önemli olayları durdurmak ve geçersiz anahtarların giriş denetimine girmesini engellemek istiyorsanız, yetki verilen olay işlemeyle bunu yapamazsınız çünkü olay yetki verilen işleyiciye ulaştığında, zaten giriş kontrolü tarafından işlendi ve bu davranışı etkilemek için çok geç.

Etkinlik temsilcisinin gerekli veya avantajlı olduğu zamanlar:

  • Olayları yakaladığınız nesneler dinamik olarak oluşturulduğunda / kaldırıldığında ve her seferinde yeni bir tane oluşturduğunuzda olay işleyicilerini açıkça yeniden bağlamak zorunda kalmadan yine de olayları yakalamak istediğinizde.
  • Hepsi aynı olay işleyiciyi isteyen çok sayıda nesneniz olduğunda (lotların en az yüzlerce olduğu). Bu durumda, kurulum zamanında yüzlerce veya daha fazla doğrudan olay işleyicisinden ziyade bir temsilci olay işleyiciyi bağlamak daha verimli olabilir. Yetkilendirilmiş olay işlemenin çalışma zamanında doğrudan olay işleyicilerinden her zaman daha az verimli olduğunu unutmayın.
  • Belgenizdeki herhangi bir öğede gerçekleşen olayları (belgenizde daha yüksek bir düzeyde) yakalamaya çalıştığınızda.
  • Tasarımınız, sayfanızdaki bir sorunu veya özelliği çözmek için açıkça etkinlik köpürme ve stopPropagation () kullanıyorsa.

Bunu biraz daha anlamak için, jQuery yetkilendirilmiş olay işleyicilerinin nasıl çalıştığını anlamak gerekir. Böyle bir şey çağırdığınızda:

$("#myParent").on('click', 'button.actionButton', myFn);

#myParentNesneye genel bir jQuery olay işleyicisi yükler . Bir tıklama olayı bu yetki verilen olay işleyiciye kadar kabarcıklar oluşturduğunda, jQuery'nin bu nesneye eklenmiş yetki verilen olay işleyicileri listesini gözden geçirmesi ve olayın kaynak öğesinin devredilen olay işleyicilerinden herhangi biriyle eşleşip eşleşmediğini görmesi gerekir.

Seçiciler oldukça dahil olabileceğinden, bu, jQuery'nin her bir seçiciyi ayrıştırması ve ardından her seçiciyle eşleşip eşleşmediğini görmek için orijinal olay hedefinin özellikleriyle karşılaştırması gerektiği anlamına gelir. Bu ucuz bir operasyon değil. Bunlardan yalnızca biri varsa önemli değil, ancak tüm seçicilerinizi belge nesnesine koyarsanız ve her kabarmış olayla karşılaştırmak için yüzlerce seçici varsa, bu olay işleme performansını ciddi şekilde engellemeye başlayabilir.

Bu nedenle, yetki verilen olay işleyicilerini, yetki verilen bir olay işleyicisinin hedef nesneye pratik kadar yakın olacak şekilde ayarlamak istersiniz. Bu, devredilen her olay işleyicisinden daha az olayın patlayacağı ve böylece performansı artıracağı anlamına gelir. Devredilen tüm olayları belge nesnesine koymak mümkün olan en kötü performanstır, çünkü tüm kabarcıklı olaylar tüm devredilen olay işleyicilerinden geçmeli ve olası tüm devredilen olay seçicilerine göre değerlendirilmelidir. Bu tam olarak neden .live()reddedildi çünkü bu böyle .live()oldu ve çok verimsiz olduğu kanıtlandı.


Böylece, optimize edilmiş performans elde etmek için:

  1. Yetki verilen olay işlemeyi yalnızca gerçekten ihtiyacınız olan bir özellik sağladığında veya performansı artırdığında kullanın. Her zaman kullanmayın çünkü kolaydır çünkü gerçekten ihtiyacınız olmadığında. Aslında olay gönderme zamanında doğrudan olay bağlama daha kötü performans gösterir.
  2. Yetki verilen olay işleyicilerini, olayın kaynağına mümkün olan en yakın üst öğeye ekleyin. Etkinlikleri yakalamak istediğiniz dinamik öğeleriniz olduğu için yetki verilmiş olay işleme kullanıyorsanız, kendisi dinamik olmayan en yakın üst öğeyi seçin.
  3. Yetki verilen olay işleyicileri için değerlendirmesi kolay seçicileri kullanın. Yetki verilen olay işlemenin nasıl çalıştığını izlediyseniz, yetki verilen bir olay işleyicisinin birçok nesneyle birçok kez karşılaştırılması gerektiğini anlayacaksınız; bu nedenle mümkün olduğunca verimli bir seçici seçmek veya nesnelerinize basit sınıflar eklemek için basit sınıflar eklemek yetki verilen olay işleme performansını artırın.

2
Muhteşem. Hızlı ve ayrıntılı açıklama için teşekkür ederim. Bu, olay gönderme zamanının .on () kullanılarak artacağı anlamlıdır, ancak artan sayfa sevk süresi ile ilk sayfa işleme ekibi arasında karar vermede hala zorlanıyorum. Genelde başlangıç ​​sayfası süresinin kısalması içindir. Örneğin, bir sayfada 200 öğem varsa (ve etkinlikler gerçekleştikçe daha fazla), tek bir olay dinleyicisi eklemek yerine, ilk yükte (100 olay dinleyicisi eklemesi gerektiğinden) yaklaşık 100 kat daha pahalıdır Ana konteyner bağlantısı
Buck

Ayrıca beni ikna ettiğinizi söylemek istedim - Tüm olayları $ (belge) 'e bağlamayın. Değişmeyen en yakın ebeveyne mümkün olduğunca bağlanın. Olay yetkilendirmesinin bir olayı doğrudan bir öğeye bağlamaktan daha iyi olduğu durumlarda yine de duruma göre karar vermem gerekecek. Ve dinamik içeriğe bağlı olaylara ihtiyaç duyduğumda (değişmeyen bir ebeveyni yoktur) @Vega'nın aşağıda önerdiğini yapmaya devam edeceğim ve yalnızca "içerikler eklendikten sonra işleyicileri / bağları ( ) DOM ", jQuery'nin context parametresini seçicimde kullanarak.
Buck

5
@ user1394692 - neredeyse aynı öğeleriniz varsa, onlar için yetki verilen olay işleyicilerini kullanmak mantıklı olabilir. Bunu cevabımda söylüyorum. Eğer dev bir 500 satırlık masam vardı ve belirli bir sütunun her satırında aynı butona sahip olsaydım, tüm düğmeleri sunmak için masanın üzerinde bir temsilci olay işleyici kullanırdım. Ancak 200 öğem vardı ve hepsinin kendi benzersiz olay işleyicisine ihtiyaçları varsa, 200 yetki verilmiş olay işleyicileri yüklemek, 200 doğrudan bağlı olay işleyicisini kurmaktan daha hızlı değildir, ancak yetki verilen olay işleme olay yürütme üzerine çok daha yavaş olabilir.
jfriend00

Mükemmelsin. Tamamen katılıyorum! Bunun yapabileceğim en kötü şey olduğunu anlıyor muyum $(document).on('click', '[myCustomAttr]', function () { ... });?
Artem Fitiskin

Tüm elemanlar dinamik olarak oluşturulduğundan / kaldırıldığından (ilk sayfa yüklemesinin dışında) pjax / turbolinklerin kullanılması ilginç bir sorun oluşturur. Bu yüzden daha iyi bir kez tüm olayları bağlamak için document, ya bağlama konusunda daha açık seçimlere page:loadve unbind Bu olaylar page:after-remove? Hmmm
Dom Christie

9

Olay yetkisi, öğeyi DOM'da var olmadan önce işleyicilerinizi yazma tekniğidir. Bu yöntemin kendi dezavantajları vardır ve sadece bu tür gereksinimleriniz varsa kullanılmalıdır.

Etkinlik delegasyonunu ne zaman kullanmalısınız?

  1. Aynı işlevselliğe ihtiyaç duyan daha fazla öğe için ortak bir işleyici bağladığınızda. (Örn: tablo satırının üzerine gelme)
    • Örnekte, tüm satırları doğrudan bağlama kullanarak bağlamak zorunda olsaydınız, bu tablodaki n satır için n işleyici oluşturursunuz. Yetkilendirme yöntemini kullanarak, bunların hepsini 1 basit işleyicide ele alabilirsiniz.
  2. Dinamik içeriği DOM'da daha sık eklediğinizde (Örn: Tabloya satır ekle / kaldır)

Etkinlik heyetini neden kullanmamalısınız?

  1. Olay yetkilendirmesi, olayı doğrudan öğeye bağlama ile karşılaştırıldığında daha yavaştır.
    • Hedef seçiciyi vurduğu her baloncukta karşılaştırır, karşılaştırma karmaşık olduğu kadar pahalı olur.
  2. Bağlandığı öğeye ulaşana kadar köpüren olay üzerinde denetim yok.

Not: Dinamik içerikler için bile, içerikler DOM'a eklendikten sonra işleyiciyi bağlarsanız olay yetkilendirme yöntemini kullanmanız gerekmez. (Dinamik içerik eklenirse sık sık kaldırılmaz / yeniden eklenmez)



0

Jfriend00'ün cevabına bazı açıklamalar ve karşı-değişkenler eklemek istiyorum. (çoğunlukla sadece bağırsak hislerime dayanan görüşlerim)

Hayır - yetki verilen tüm olay işleyicilerini belge nesnesine BAĞLAMAMALISINIZ. Bu muhtemelen oluşturabileceğiniz en kötü performans senaryosudur.

Öncelikle, etkinlik devri kodunuzu her zaman daha hızlı hale getirmez. Bazı durumlarda, avantajlıdır ve bazı durumlarda değildir. Etkinlik yetkilendirmesine gerçekten ihtiyaç duyduğunuzda ve bundan yararlanabiliyorsanız etkinlik yetkisi kullanmalısınız. Aksi takdirde, genellikle daha verimli olacağından, olay işleyicilerini doğrudan olayın gerçekleştiği nesnelere bağlamalısınız.

Tek bir öğe için kayıt ve etkinlik yapacaksanız performansın biraz daha iyi olabileceği doğru olsa da, delegasyonun getirdiği ölçeklenebilirlik avantajlarına karşı ağırlık vermediğine inanıyorum. Ayrıca, bunun bir kanıtı olmamasına rağmen, tarayıcıların bunu daha verimli bir şekilde ele alacağına inanıyorum. Benim görüşüme göre, etkinlik heyeti gidilecek yoldur!

İkinci olarak, temsil edilen tüm olayları belge düzeyinde BAĞLAMAMALISINIZ. İşte tam da bu nedenle .live () kullanımdan kaldırılmıştır, çünkü bu şekilde çok sayıda olayınız olduğunda bu çok verimsizdir. Yetki verilen olay işleme için, bunları dinamik olmayan en yakın ebeveyne bağlamak çok daha verimlidir.

Buna katılıyorum. Bir etkinliğin yalnızca bir kap içinde olacağından% 100 eminseniz, olayı bu kapsayıcıya bağlamak mantıklıdır, ancak yine de olayları doğrudan tetikleyici öğeye bağlamaya karşı çıkacağım.

Üçüncü olarak, tüm olaylar işe yaramaz veya tüm sorunlar yetki devri ile çözülemez. Örneğin, bir giriş denetimindeki önemli olayları durdurmak ve geçersiz anahtarların giriş denetimine girmesini engellemek istiyorsanız, yetki verilen olay işlemeyle bunu yapamazsınız çünkü olay yetki verilen işleyiciye ulaştığında, zaten giriş kontrolü tarafından işlendi ve bu davranışı etkilemek için çok geç.

Bu doğru değil. Lütfen bu kodu inceleyinPen: https://codepen.io/pwkip/pen/jObGmjq

document.addEventListener('keypress', (e) => {
    e.preventDefault();
});

Dokümanda keypress olayını kaydederek kullanıcının yazmasını nasıl önleyebileceğinizi gösterir.

Etkinlik temsilcisinin gerekli veya avantajlı olduğu zamanlar:

Olayları yakaladığınız nesneler dinamik olarak oluşturulduğunda / kaldırıldığında ve her seferinde yeni bir tane oluşturduğunuzda olay işleyicilerini açıkça yeniden bağlamak zorunda kalmadan yine de olayları yakalamak istediğinizde. Hepsi aynı olay işleyiciyi isteyen çok sayıda nesneniz olduğunda (lotların en az yüzlerce olduğu). Bu durumda, kurulum zamanında yüzlerce veya daha fazla doğrudan olay işleyicisinden ziyade bir temsilci olay işleyiciyi bağlamak daha verimli olabilir. Yetkilendirilmiş olay işlemenin çalışma zamanında doğrudan olay işleyicilerinden her zaman daha az verimli olduğunu unutmayın.

Bu alıntıya https://ehsangazar.com/optimizing-javascript-event-listeners-for-performance-e28406ad406c adresinden yanıt vermek istiyorum.

Event delegation promotes binding as few DOM event handlers as possible, since each event handler requires memory. For example, let’s say that we have an HTML unordered list we want to bind event handlers to. Instead of binding a click event handler for each list item (which may be hundreds for all we know), we bind one click handler to the parent unordered list itself.

Ayrıca, googling performance cost of event delegation google, etkinlik delegasyonu lehine daha fazla sonuç döndürür.

Belgenizdeki herhangi bir öğede gerçekleşen olayları (belgenizde daha yüksek bir düzeyde) yakalamaya çalıştığınızda. Tasarımınız, sayfanızdaki bir sorunu veya özelliği çözmek için açıkça etkinlik köpürme ve stopPropagation () kullanıyorsa. Bunu biraz daha anlamak için, jQuery yetkilendirilmiş olay işleyicilerinin nasıl çalıştığını anlamak gerekir. Böyle bir şey çağırdığınızda:

$ ("# myParent"). üzerinde ('tıklayın', 'button.actionButton', myFn); #MyParent nesnesine genel bir jQuery olay işleyicisi yükler. Bir tıklama olayı bu yetki verilen olay işleyiciye kadar kabarcıklar oluşturduğunda, jQuery'nin bu nesneye eklenmiş yetki verilen olay işleyicileri listesini gözden geçirmesi ve olayın kaynak öğesinin devredilen olay işleyicilerinden herhangi biriyle eşleşip eşleşmediğini görmesi gerekir.

Seçiciler oldukça dahil olabileceğinden, bu, jQuery'nin her bir seçiciyi ayrıştırması ve ardından her seçiciyle eşleşip eşleşmediğini görmek için orijinal olay hedefinin özellikleriyle karşılaştırması gerektiği anlamına gelir. Bu ucuz bir operasyon değil. Bunlardan yalnızca biri varsa önemli değil, ancak tüm seçicilerinizi belge nesnesine koyarsanız ve her kabarmış olayla karşılaştırmak için yüzlerce seçici varsa, bu olay işleme performansını ciddi şekilde engellemeye başlayabilir.

Bu nedenle, yetki verilen olay işleyicilerini, yetki verilen bir olay işleyicisinin hedef nesneye pratik kadar yakın olacak şekilde ayarlamak istersiniz. Bu, devredilen her olay işleyicisinden daha az olayın patlayacağı ve böylece performansı artıracağı anlamına gelir. Devredilen tüm olayları belge nesnesine koymak mümkün olan en kötü performanstır, çünkü tüm kabarcıklı olaylar tüm devredilen olay işleyicilerinden geçmeli ve olası tüm devredilen olay seçicilerine göre değerlendirilmelidir. İşte tam olarak bu nedenle .live () kullanımdan kaldırılmıştır çünkü .live () bunu yaptı ve çok verimsiz olduğunu kanıtladı.

Bu nerede belgeleniyor? Bu doğruysa, o zaman jQuery yetkiyi çok verimsiz bir şekilde ele alıyor gibi görünüyor, ve sonra karşı argümanlarım sadece vanilya JS'ye uygulanmalı.

Yine de: Bu iddiayı destekleyen resmi bir kaynak bulmak istiyorum.

:: DÜZENLE ::

JQuery gerçekten olay verimsiz bir şekilde köpürme yapıyor gibi görünüyor (IE8'i destekledikleri için) https://api.jquery.com/on/#event-performance

Buradaki argümanlarımın çoğu sadece vanilya JS ve modern tarayıcılar için geçerli.

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.