Прямий та делегований - jQuery .on ()


159

Я намагаюся зрозуміти цю особливу різницю між прямими та делегованими обробниками подій, використовуючи метод jQuery .on () . Зокрема, останнє речення в цьому пункті:

Коли а selectorнадається, обробник події називається делегованим . Обробник не викликається, коли подія відбувається безпосередньо на зв'язаному елементі, а лише для нащадків (внутрішніх елементів), які відповідають селектору. jQuery перетворює подію від цілі події аж до елемента, до якого приєднаний обробник (тобто, внутрішній до самого зовнішнього елемента) і запускає обробник для будь-яких елементів по цьому шляху, що відповідає селектору.

Що означає "запускає обробник для будь-яких елементів"? Я зробив тестову сторінку для експерименту з концепцією. Але обидві наступні конструкції призводять до однакової поведінки:

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

або,

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

Може, хтось міг би звернутися до іншого прикладу, щоб уточнити цей момент? Дякую.


7
Для всіх зацікавлених: jsperf.com/jquery-fn-on-delegate-vs-direct
dgo

1
@KevinWheeler Я прокоментував вашу загадку нижче, але тут , по суті, вона не налаштована правильно (ви прив'язуєтесь до батьківського елемента, а делегування призначене для дітей). Щоб відповісти на ваше запитання moey, це означає, що делегований обробник буде відповідати нещодавно доданим елементам, де той, без делегування, не буде. Делегація має перевагу в тому, що у браузері задіяно менше подій, що спричиняє менший обсяг пам’яті для програми, однак компроміс - це збільшує час на обробку кліку (мінімально). Якщо ви робите гру, не делегуйте.
vipero07

1
"Тестова сторінка", на яку ви посилаєтесь, не працює.
Хайме Монтоя

Відповіді:


373

Випадок 1 (прямий):

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

== Гей! Я хочу, щоб кожен span.green усередині div # target прослухав: коли на вас натискають, зробіть X.

Випадок 2 (делегований):

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

== Гей, div # target! Коли натискатимуть будь-який із ваших дочірніх елементів, які є "span.green", робіть X з ними.

Іншими словами...

У випадку 1, кожному з цих проміжків були надані інструкції індивідуально. Якщо будуть створені нові проміжки, вони не почують інструкцію і не відповідатимуть на кліки. Кожен проміжок безпосередньо відповідає за власні події.

У випадку 2, тільки контейнер отримав інструкцію; вона несе відповідальність за те, щоб помітити кліки від імені своїх дочірніх елементів. Робота з лову подій делегована . Це також означає, що інструкція буде виконуватися для дочірніх елементів, які створюються в майбутньому.


45
Це чудове пояснення і вніс ясність у питання, яке я давно не хочу зрозуміти. Дякую!
КГВР

3
То чому ж on()допускаються два аргументи, коли це в значній мірі збігається з використанням click()?
nipponese

5
.on () - API загального призначення, який може обробляти будь-який тип події, включаючи безліч різних подій (ви можете помістити кілька імен подій у цю першу рядок.) .click () - це лише скорочення для цієї першої форми.
N3dst4

1
@newbie, @ N3dst4: e.targetбуде початковою ціллю події клацання (може бути дочірнім вузлом, якщо span.greenмає дітей). Зсередини обробника, ви повинні використовувати thisпосилання. Дивіться цю загадку .
LeGEC

1
Ще одна примітка, яку слід додати до теми делегації - це і дуже корисно, і дуже ефективно. Ви будете використовувати менше ресурсів веб-переглядача, делегуючи проти приєднання події до кожного відповідного елемента. Я думав, що це я згадаю, про всяк випадок, якщо людям потрібно більше причин використовувати делегацію.
phatskat

6

Перший спосіб, $("div#target span.green").on()прив'язує обробник клацань безпосередньо до прольотів (ів), які відповідають селектору в момент виконання коду. Це означає, що якщо інші проміжки будуть додані пізніше (або змінили клас на відповідність), вони пропустили і не матимуть обробника кліків. Це також означає, що якщо пізніше вилучити "зелений" клас з одного з проміжків, його обробник клацань буде продовжувати працювати - jQuery не відслідковує, як призначений обробник, і перевіряє, чи не відповідає селектор.

Другий спосіб, $("div#target").on()прив'язує обробник кліків до дів (ів), які відповідають (знову ж таки, це проти тих, які відповідають на той момент), але коли натискання відбувається десь у діві, функція обробника запускається лише у випадку, якщо клацання траплявся не просто у div, а у дочірньому елементі, що відповідає селектору другого параметра параметру .on()"span.green". Зроблено таким чином, не має значення, коли були створені ці дочірні проміжки, натискання на них все одно запустить обробник.

Отже, для сторінки, яка не додає та не змінює її вміст, ви не помітите різниці між двома методами. Якщо ви динамічно додаєте додаткові дочірні елементи, другий синтаксис означає, що вам не доведеться турбуватися про призначення їм обробників кліків, тому що ви вже робили це один раз у батьків.


5

Пояснення N3dst4 ідеально. Виходячи з цього, можна припустити, що всі дочірні елементи знаходяться всередині тіла, тому нам потрібно використовувати лише це:

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

Він працює з прямою або делегатною подією.


2
jquery не радить використовувати body, оскільки це повільніше, оскільки сценарій повинен шукати всіх дітей всередині тіла, яких у більшості випадків має бути багато. Краще (швидше) використовувати проміжний батьківський контейнер елемента.
mikesoft

1
Jquery видалив живий метод, який робив те саме, що ви показали. Це низька продуктивність, і її не слід використовувати.
Мацей Сікора

У сучасних веб-переглядачах $('body').on()делегування з сайту .elementповинно вести себе точно так само, як document.body.addEventHandler()і if (Event.target.className.matches(/\belement\b/))у вихідних, із зворотним викликом. Це може бути дуже повільно у jquery через $.proxyнакладні витрати, але не цитуйте мене з цього приводу.
ковбер

2

Дотична до ОП, але концепція, яка допомогла мені розгадати плутанину з цією особливістю, полягає в тому, що зв'язані елементи повинні бути батьками вибраних елементів .

  • Пов’язане означає те, що залишилося від .on.
  • Вибране посилається на 2-й аргумент .on().

Делегування не працює як .find (), вибираючи підмножину зв'язаних елементів. Селектор стосується лише суворих дочірніх елементів.

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

дуже відрізняється від

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

Зокрема, щоб отримати переваги @ N3dst4 натякає на "елементи, які створюються в майбутньому", пов'язаний елемент повинен бути постійним батьківським . Тоді вибрані діти можуть приходити і їхати.

EDIT

Контрольний список того, чому делегований .onне працює

Хитрі причини, чому $('.bound').on('event', '.selected', some_function)може не працювати:

  1. Зв'язаний елемент не є постійним . Він був створений після дзвінка.on()
  2. Вибраний елемент не є належним дочірнім зв'язаним елементом. Це той самий елемент.
  3. Вибраний елемент перешкоджав переповненню події на прив'язаний елемент викликом .stopPropagation().

(Пропускаючи менш складні причини, наприклад, неправильно написаний перемикач.)


1

Я розгнівав пост із порівнянням прямих подій та делегованих. Я порівнюю чистий js, але він має те саме значення для jquery, який лише інкапсулює його.

Висновок полягає в тому, що делегована обробка подій призначена для динамічної структури DOM, де прив’язані елементи можуть створюватися під час взаємодії користувача зі сторінкою (не потрібно знову прив’язки), а пряма обробка подій - для статичних елементів DOM, коли ми знаємо, що структура не зміниться.

Для отримання додаткової інформації та повного порівняння - http://maciejsikora.com/standard-events-vs-event-delegation/

Використання завжди делегованих обробників, які, на мою думку, зараз дуже модні, не є правильним способом, багато програмістів використовують його, оскільки "це слід використовувати", але правда полягає в тому, що обробники прямих подій краще для певної ситуації та вибір, який метод слід підтримувати шляхом пізнання відмінностей.


якщо приєднувати багато обробників подій (наприклад, кожен рядок таблиці), часто краще для продуктивності використовувати один делегований обробник для контейнера, а не додавати багато прямих обробників. Це можна побачити з основного факту, що кількість обробників подій сама по собі є метрикою профілювання.
ковбер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.