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

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

Если указан selector, обработчик событий называется делегированным. Обработчик не вызывается, когда событие происходит непосредственно на привязанном элементе, а только для потомков (внутренних элементов), которые соответствуют селектору. jQuery перемещает событие от цели события до элемента, к которому прикреплен обработчик (т. е. от самого внутреннего до самого внешнего элемента), и запускает обработчик для любых элементов вдоль этого пути, соответствующих селектору.

Что значит «запускать обработчик любых элементов»? Я создал тестовую страницу, чтобы поэкспериментировать с этой концепцией. Но обе следующие конструкции приводят к одинаковому поведению:

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

or,

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

Может быть, кто-нибудь может сослаться на другой пример, чтобы прояснить этот момент? Спасибо.


person moey    schedule 13.11.2011    source источник
comment
Для всех заинтересованных: jsperf.com/jquery-fn-on-delegate-vs- прямой   -  person dgo    schedule 07.04.2015
comment
@KevinWheeler Я прокомментировал вашу скрипку ниже, но здесь, по сути, она настроена неправильно (вы обязываете к родительскому элементу, а делегирование предназначено для дочерних элементов). Чтобы ответить на ваш вопрос, это означает, что делегированный обработчик будет соответствовать вновь добавленным элементам, тогда как тот, который без делегирования, не будет. Преимущество делегирования состоит в том, что в браузере регистрируется меньше событий, вызывающих меньшее потребление памяти для приложения, однако компромисс заключается в том, что увеличивается время обработки щелчка (минимально). Если вы делаете игру, не делегируйте.   -  person vipero07    schedule 20.06.2015
comment
Тестовая страница, на которую вы ссылаетесь, не работает.   -  person Jaime Montoya    schedule 12.09.2017


Ответы (6)


Случай 1 (прямой):

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

== Привет! Я хочу, чтобы каждый span.green внутри цели div # слушал: когда вы нажимаете, делайте X.

Случай 2 (делегированный):

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

== Привет, div # target! Когда щелкают по любому из ваших дочерних элементов, которые являются "span.green", сделайте X с ними.

Другими словами ...

В случае 1 каждому из этих пролетов были даны индивидуальные инструкции. Если будут созданы новые диапазоны, они не услышат инструкции и не будут реагировать на щелчки. Каждый диапазон непосредственно отвечает за свои события.

В случае 2 указание дано только контейнеру; он отвечает за обнаружение кликов от имени своих дочерних элементов. Работа по отлову событий была делегирована. Это также означает, что инструкция будет выполняться для дочерних элементов, которые будут созданы в будущем.

person N3dst4    schedule 13.11.2011
comment
Это прекрасное объяснение, которое внесло ясность в проблему, которую я долго отказывался понять. Спасибо! - person dgo; 13.04.2013
comment
Так почему on() допускает два аргумента, если это почти то же самое, что и click()? - person nipponese; 01.08.2013
comment
.on () - это API общего назначения, который может обрабатывать события любого типа, включая несколько различных событий (вы можете поместить несколько имен событий в эту первую строку.) .click () - это просто сокращение для этой первой формы. - person N3dst4; 01.08.2013
comment
Также, чтобы добавить к идее делегирования. Скажем, в будущем у вас будет больше элементов с именем span.green, добавленных в #target, эти элементы span.green также будут запускать эту функцию. - person CharlieM; 08.10.2013
comment
@ N3dst4 У меня вопрос по этому поводу. Как мне узнать, какой span.green щелкнуть таким образом? - person newbie; 08.11.2013
comment
@newbie - обратный вызов может принимать аргумент события, а событие имеет .target: $ (div # target) .on (click, span.green, function (event) {// что-то делать с event.target}); - person N3dst4; 08.11.2013
comment
@ N3dst4 Понятно, еще один вопрос. Как насчет того, чтобы span.green было много, тогда щелкнули по одному. Как мне выполнить такое условие: if( not the clicked element ){ // do something } примечание: все они имеют один и тот же класс. - person newbie; 08.11.2013
comment
@newbie Можете перефразировать вопрос? - person N3dst4; 08.11.2013
comment
@ N3dst4 извините, если это сбивает с толку. Принимая во внимание это $("div#target").on("click", "span.green", function() {...});, допустим, что span.green ссылается на 3 элемента span на div#target, а тот, который был нажат, является вторым элементом. Как я могу сделать другой диапазон исключающим диапазон (2-й диапазон), который был нажат, чтобы что-то сделать. - person newbie; 08.11.2013
comment
О, я вижу. Это почти отдельный вопрос, но вы можете сделать это: $("div#target span.green").not(event.target).whatever() - person N3dst4; 08.11.2013
comment
@ N3dst4 Понятно, значит, есть функция .not(). Спасибо за помощь. - person newbie; 08.11.2013
comment
@newbie, @ N3dst4: e.target будет начальной целью события щелчка (может быть дочерним узлом, если у span.green есть дочерние элементы). Изнутри обработчика вы должны использовать ссылку this. См. эту скрипку. - person LeGEC; 14.02.2014
comment
Еще одно замечание к теме делегирования - это одновременно очень полезно и очень эффективно. Вы будете использовать меньше ресурсов браузера с делегированием по сравнению с присоединением события к каждому совпадающему элементу. Подумал, что упомяну об этом, на всякий случай, если людям понадобится больше причин для использования делегирования. - person phatskat; 13.03.2014
comment
как насчет спектаклей? Выполняется ли поиск в глубину родительской DOM при каждом нажатии? - person Julio Guerra; 30.09.2014
comment
jQuery использует всплытие событий (подробности см. в quirksmode.org/js/events_order.html) , поэтому обработчики на самом деле запускаются сначала дочерними. Так что в некотором смысле делегированные обработчики вызывают больше работы, потому что поиск должен идти вверх по дереву, чтобы найти обработчик. Однако: на самом деле это довольно быстрая операция, даже с глубокой DOM, и экономия, которую вы получаете в плане отсутствия необходимости создавать и связывать новые обработчики, когда ваша DOM изменяется, перевешивает разницу. - person N3dst4; 30.09.2014
comment
@ N3dst4 - пожалуйста, исправьте книгу, отвечая на вопросы по всем подобным темам. Для тех, кто учится, подобная аналогия просто фантастична. Я аплодирую этому ответу и не могу вас отблагодарить! - person K7Buoy; 17.02.2017
comment
@nipponese Если вы прочитали следующий синтаксис на странице api.jquery.com/on и значение каждого из них параметр, вы найдете ответ на свой вопрос: .on (events [, selector] [, data], handler). Как видите, первый параметр - это события, и в официальном объяснении говорится, что это тип String, и добавлено следующее определение: «Один или несколько типов событий, разделенных пробелами, и необязательные пространства имен, такие как click или keydown.myPlugin». Как сказал N3dst4 в своем комментарии, .on () - это API общего назначения, который может обрабатывать любые события. Первый параметр может быть, например, следующим: нажмите "Отправить". - person Jaime Montoya; 12.09.2017

Первый способ, $("div#target span.green").on(), связывает обработчик кликов непосредственно с диапазоном (ами), который соответствует селектору в момент выполнения кода. Это означает, что если позже будут добавлены другие промежутки (или их класс будет изменен для соответствия), они пропущены и не будут иметь обработчика кликов. Это также означает, что если вы позже удалите «зеленый» класс из одного из диапазонов, его обработчик кликов продолжит работу - jQuery не отслеживает, как был назначен обработчик, и проверяет, соответствует ли селектор.

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

Таким образом, для страницы, которая не добавляет или не изменяет свое содержимое динамически, вы не заметите разницы между этими двумя методами. Если вы динамически добавляете дополнительные дочерние элементы, второй синтаксис означает, что вам не нужно беспокоиться о назначении им обработчиков кликов, потому что вы уже сделали это один раз для родительского элемента.

person nnnnnn    schedule 13.11.2011

Объяснение N3dst4 прекрасно. Исходя из этого, мы можем предположить, что все дочерние элементы находятся внутри body, поэтому нам нужно использовать только это:

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

Он работает с прямым или делегированным событием.

person Brynner Ferreira    schedule 25.04.2014
comment
jquery не рекомендует использовать тело, так как он работает медленнее, потому что скрипту придется искать все дочерние элементы внутри тела, что в большинстве случаев должно быть много. Лучше (быстрее) использовать непосредственный родительский контейнер элемента. - person mikesoft; 29.04.2015
comment
Jquery удалил живой метод, который делал то же самое, что и вы. Это слабая производительность, и ее не следует использовать. - person Maciej Sikora; 06.09.2016
comment
В современных браузерах $('body').on() делегирование от .element должно вести себя точно так же, как нативное document.body.addEventHandler() с if (Event.target.className.matches(/\belement\b/)) в обратном вызове. Это может быть немного медленнее в jquery из-за $.proxy накладных расходов, но не цитируйте меня по этому поводу. - person cowbert; 12.01.2018

Тангенциально к OP, но идея, которая помогла мне разгадать путаницу с этой функцией, заключается в том, что связанные элементы должны быть родителями выбранных элементов.

  • Связанный относится к тому, что осталось от .on.
  • Выбрано относится ко второму аргументу .on().

Делегирование не работает как .find (), выбирая подмножество связанных элементов. Селектор применяется только к строгим дочерним элементам.

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

сильно отличается от

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

В частности, чтобы получить преимущества, на которые указывает @ N3dst4 с «элементами, которые будут созданы в будущем», связанный элемент должен быть постоянным родительским элементом. Затем выбранные дети могут приходить и уходить.

РЕДАКТИРОВАТЬ

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

Коварные причины, по которым $('.bound').on('event', '.selected', some_function) может не работать:

  1. Связанный элемент не является постоянным. Он был создан после вызова .on()
  2. Выбранный элемент не является правильным потомком связанного элемента. Это тот же элемент.
  3. Выбранный элемент предотвратил всплытие события в связанный элемент, вызвав .stopPropagation().

(Опуская менее сложные причины, такие как неправильный выбор селектора.)

person Bob Stein    schedule 27.01.2016

Написал пост со сравнением прямых и делегированных мероприятий. Я сравниваю чистый js, но он имеет то же значение для jquery, который только инкапсулирует его.

Вывод состоит в том, что делегированная обработка событий предназначена для динамической структуры DOM, где связанные элементы могут быть созданы, пока пользователь взаимодействует со страницей (нет необходимости снова связывать), а прямая обработка событий предназначена для статических элементов DOM, когда мы знаем, что структура не изменится.

Для получения дополнительной информации и полного сравнения - http://maciejsikora.com/standard-events-vs-event-delegation/

Использование всегда делегированных обработчиков, которые, как я вижу, в настоящее время очень модно, - неправильный путь, многие программисты используют его, потому что «его следует использовать», но правда в том, что прямые обработчики событий лучше для некоторых ситуаций и выбор того, какой метод следует использовать. знанием различий.

person Maciej Sikora    schedule 06.09.2016
comment
при присоединении множества обработчиков событий (например, каждой строки таблицы) часто с большей производительностью лучше использовать один делегированный обработчик к контейнеру вместо присоединения множества прямых обработчиков. Вы можете видеть это из того основного факта, что количество обработчиков событий само по себе является показателем профилирования. - person cowbert; 12.01.2018

Случай 3 (делегированный):

$("div#target").delegate("span.green", "click", function() {...});
person Leo    schedule 11.01.2021