Bezpośrednie a delegowane - jQuery .on ()

159

Staram się zrozumieć tę szczególną różnicę między bezpośrednich i delegowanych obsługi zdarzeń przy użyciu jQuery .on () metody . W szczególności ostatnie zdanie w tym akapicie:

Gdy selectorpodano a, program obsługi zdarzeń jest określany jako delegowany . Procedura obsługi nie jest wywoływana, gdy zdarzenie występuje bezpośrednio w powiązanym elemencie, ale tylko w przypadku elementów podrzędnych (elementów wewnętrznych), które pasują do selektora. jQuery przesuwa zdarzenie z miejsca docelowego zdarzenia do elementu, do którego jest dołączona procedura obsługi (tj. najbardziej wewnętrzna do najbardziej zewnętrznego elementu) i uruchamia procedurę obsługi dla wszystkich elementów wzdłuż tej ścieżki pasujących do selektora.

Co to znaczy „uruchamia procedurę obsługi dla dowolnych elementów”? Zrobiłem stronę testową, aby poeksperymentować z tą koncepcją. Ale obie poniższe konstrukcje prowadzą do tego samego zachowania:

$("div#target span.green").on("click", function() {
   alert($(this).attr("class") + " is clicked");
});

lub,

$("div#target").on("click", "span.green", function() {
   alert($(this).attr("class") + " is clicked");
});

Może ktoś mógłby odnieść się do innego przykładu, aby wyjaśnić tę kwestię? Dzięki.

moey
źródło
7
Dla wszystkich zainteresowanych: jsperf.com/jquery-fn-on-delegate-vs-direct
dgo
1
@KevinWheeler Komentowałem poniżej twoje skrzypce, ale tutaj zasadniczo nie są one poprawnie skonfigurowane ( wiążesz się z elementem nadrzędnym, a delegacja jest przeznaczona dla dzieci). Odpowiadając na twoje pytanie, oznacza to, że delegowana procedura obsługi dopasuje nowo dodane elementy, podczas gdy ta bez delegacji nie. Zaletą delegowania jest to, że mniej zdarzeń jest podłączonych do przeglądarki, co powoduje mniejsze zużycie pamięci przez aplikację, jednak kompromis polega na tym, że wydłuża czas przetwarzania kliknięcia (minimalnie). Jeśli tworzysz grę, nie deleguj.
vipero
1
„Strona testowa”, do której się odwołujesz, nie działa.
Jaime Montoya,

Odpowiedzi:

373

Przypadek 1 (bezpośredni):

$("div#target span.green").on("click", function() {...});

== Hej! Chcę, aby każdy span.green wewnątrz celu div # nasłuchiwał: kiedy zostaniesz kliknięty, zrób X.

Przypadek 2 (delegowany):

$("div#target").on("click", "span.green", function() {...});

== Hej, div # target! Kiedy którykolwiek z elementów podrzędnych „span.green” zostanie kliknięty, zrób z nimi X.

Innymi słowy...

W przypadku 1 każdemu z tych przęseł podano indywidualne instrukcje. Jeśli zostaną utworzone nowe zakresy, nie usłyszą instrukcji i nie będą reagować na kliknięcia. Każdy zakres jest bezpośrednio odpowiedzialny za swoje własne zdarzenia.

W przypadku 2 instrukcję przekazano tylko kontenerowi; odpowiada za zauważanie kliknięć w imieniu swoich elementów podrzędnych. Praca nad łapaniem zdarzeń została oddelegowana . Oznacza to również, że instrukcja zostanie wykonana dla elementów potomnych, które zostaną utworzone w przyszłości.

N3dst4
źródło
45
To wspaniałe wyjaśnienie i wyjaśniło kwestię, której od dawna nie chciałem zrozumieć. Dzięki!
dgo
3
Dlaczego więc on()dopuszcza się dwa argumenty, skoro byłoby to prawie takie samo jak używanie click()?
nipponese
5
.on () to interfejs API ogólnego przeznaczenia, który może obsłużyć wszelkiego rodzaju zdarzenia, w tym wiele różnych zdarzeń (w pierwszym ciągu można umieścić wiele nazw zdarzeń) .click () jest po prostu skrótem dla tej pierwszej postaci.
N3dst4
1
@newbie, @ N3dst4: e.targetbędzie początkowym celem zdarzenia kliknięcia (może być węzłem podrzędnym, jeśli span.greenma dzieci). Z wnętrza programu obsługi należy użyć thisodwołania. Zobacz to skrzypce .
LeGEC
1
Kolejna uwaga do dodania do tematu delegacji - jest to zarówno bardzo przydatne, jak i bardzo wydajne. Zużyjesz mniej zasobów przeglądarki przy delegowaniu zamiast dołączania zdarzenia do każdego pasującego elementu. Pomyślałem, że o tym wspomnę, na wypadek gdyby ludzie potrzebowali więcej powodów, by korzystać z delegacji.
phatskat
6

Pierwszy sposób $("div#target span.green").on()wiąże procedurę obsługi kliknięcia bezpośrednio z zakresami, które pasują do selektora w momencie wykonywania kodu. Oznacza to, że jeśli inne rozpiętości zostaną dodane później (lub zmienisz ich klasę, aby pasowały), przegapią one i nie będą miały modułu obsługi kliknięć. Oznacza to również, że jeśli później usuniesz „zieloną” klasę z jednego z zakresów, jego moduł obsługi kliknięć będzie nadal działał - jQuery nie śledzi sposobu przypisania modułu obsługi i sprawdza, czy selektor nadal pasuje.

Drugi sposób $("div#target").on(),, wiąże moduł obsługi kliknięcia z pasującymi elementami div (znowu, jest to przeciwko tym, które pasują w danym momencie), ale kiedy nastąpi kliknięcie gdzieś w elemencie div, funkcja obsługi zostanie uruchomiona tylko wtedy, gdy kliknięcie wystąpiło nie tylko w elemencie div, ale w elemencie podrzędnym pasującym do selektora w drugim parametrze do .on()„span.green”. W ten sposób nie ma znaczenia, kiedy te rozpiętości podrzędne zostały utworzone, kliknięcie ich nadal będzie uruchamiać procedurę obsługi.

Dlatego w przypadku strony, która nie dodaje ani nie zmienia swojej zawartości dynamicznie, nie zauważysz różnicy między tymi dwiema metodami. Jeśli dynamicznie dodajesz dodatkowe elementy podrzędne, druga składnia oznacza, że ​​nie musisz się martwić o przypisywanie do nich programów obsługi kliknięć, ponieważ już raz to zrobiłeś w nadrzędnym.

nnnnnn
źródło
5

Wyjaśnienie N3dst4 jest doskonałe. Na tej podstawie możemy założyć, że wszystkie elementy potomne znajdują się wewnątrz ciała, dlatego musimy użyć tylko tego:

$('body').on('click', '.element', function(){
    alert('It works!')
});

Działa z wydarzeniem bezpośrednim lub delegowanym.

Brynner Ferreira
źródło
2
jquery odradza używanie body, ponieważ jest wolniejsze, ponieważ skrypt musiałby szukać wszystkich dzieci wewnątrz body, co w większości przypadków powinno być dużo. Lepiej (szybciej) użyć pośredniego kontenera nadrzędnego elementu.
mikesoft
1
Jquery usunął metodę na żywo, która działała tak samo, jak pokazałeś. To jest słaba wydajność i nie powinno być używane.
Maciej Sikora
W nowoczesnych przeglądarkach $('body').on()delegacja z .elementpowinna zachowywać się dokładnie tak samo jak natywna document.body.addEventHandler()z elementem if (Event.target.className.matches(/\belement\b/))wywołania zwrotnego. Może być trochę wolniejszy w jQuery z powodu $.proxynarzutu, ale nie cytuj mnie na ten temat.
cowbert
2

Styczne do PO, ale koncepcja, która pomogła mi rozwikłać zamieszanie z tą funkcją, polega na tym, że powiązane elementy muszą być rodzicami wybranych elementów .

  • Powiązane odnosi się do tego, co zostało z .on.
  • Selected odnosi się do drugiego argumentu .on().

Delegowanie nie działa tak jak .find (), wybierając podzbiór powiązanych elementów. Selektor ma zastosowanie tylko do ścisłych elementów potomnych.

$("span.green").on("click", ...

bardzo różni się od

$("span").on("click", ".green", ...

W szczególności, aby uzyskać korzyści ze wskazówek @ N3dst4 w przypadku „elementów, które zostaną utworzone w przyszłości”, element powiązany musi być stałym elementem nadrzędnym . Następnie wybrane dzieci mogą przychodzić i wychodzić.

EDYTOWAĆ

Lista kontrolna, dlaczego delegowanie .onnie działa

Podstępne powody, dla których $('.bound').on('event', '.selected', some_function)może nie działać:

  1. Element powiązany nie jest trwały . Powstał po wywołaniu.on()
  2. Wybrany element nie jest właściwym elementem podrzędnym elementu związanego. To ten sam element.
  3. Wybrany element uniemożliwił propagację zdarzenia do powiązanego elementu przez wywołanie .stopPropagation().

(Pomijając mniej skomplikowane przyczyny, takie jak błędnie napisany selektor).

Bob Stein
źródło
1

Napisałem post z porównaniem wydarzeń bezpośrednich i delegowanych. Porównuję czysty js, ale ma to samo znaczenie dla jquery, które tylko je hermetyzuje.

Wniosek jest taki, że delegowana obsługa zdarzeń dotyczy dynamicznej struktury DOM, w której powiązane elementy mogą być tworzone podczas interakcji użytkownika ze stroną (nie ma potrzeby ponownego wiązania), a bezpośrednia obsługa zdarzeń dotyczy statycznych elementów DOM, gdy wiemy, że struktura nie ulegnie zmianie.

Więcej informacji i pełne porównanie - http://maciejsikora.com/standard-events-vs-event-delegation/

Używanie zawsze delegowanych procedur obsługi, które, jak widzę, jest obecnie bardzo modne, nie jest właściwe, wielu programistów używa go, ponieważ „powinno być używane”, ale prawda jest taka, że ​​bezpośrednie procedury obsługi zdarzeń są lepsze w niektórych sytuacjach i przy wyborze metody, która powinna być obsługiwana znajomość różnic.

Maciej Sikora
źródło
w przypadku dołączania wielu programów obsługi zdarzeń (na przykład każdego wiersza tabeli), często lepszym rozwiązaniem jest użycie jednej delegowanej procedury obsługi do kontenera zamiast dołączania wielu bezpośrednich procedur obsługi. Można to zobaczyć na podstawie podstawowego faktu, że liczba programów obsługi zdarzeń sama w sobie jest metryką profilowania.
cowbert