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

159

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

Когда selectorпредоставляется a , обработчик события называется делегированным . Обработчик вызывается не тогда, когда событие происходит непосредственно в связанном элементе, а только для потомков (внутренних элементов), которые соответствуют селектору. 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");
});

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

Moey
источник
7
Для всех заинтересованных: jsperf.com/jquery-fn-on-delegate-vs-direct
Dgo
1
@KevinWheeler Я прокомментировал вашу скрипку ниже, но здесь , по сути, она настроена неправильно (вы привязываетесь к родительскому элементу, а делегирование предназначено для детей). Чтобы ответить на ваш вопрос, моё, это означает, что делегированный обработчик будет соответствовать новым добавленным элементам, а тот, что без делегирования, не будет. Преимущество делегирования заключается в том, что в браузере подключается меньше событий, что снижает потребление памяти приложением, однако компромисс заключается в том, что он увеличивает время обработки клика (минимально). Если вы делаете игру, не делегируйте.
vipero07
1
«Тестовая страница», на которую вы ссылаетесь, не работает.
Хайме Монтойя

Ответы:

374

Случай 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 только контейнер был дан инструкции; он отвечает за уведомление о кликах от имени своих дочерних элементов. Работа по отлову событий была делегирована . Это также означает, что инструкция будет выполняться для дочерних элементов, которые будут созданы в будущем.

N3dst4
источник
46
Это отличное объяснение, и оно внесло ясность в проблему, которую я давно отказался понять. Спасибо!
КГВР
3
Так почему же on()допускается два аргумента, когда это в значительной степени совпадает с использованием click()?
ниппонец
5
.on () - это API общего назначения, который может обрабатывать любые события, включая несколько разных событий (вы можете поместить несколько имен событий в эту первую строку.) .click () - это просто сокращение для этой первой формы.
N3dst4
1
@newbie, @ N3dst4: e.targetбудет исходной целью события click (может быть дочерним узлом, если у span.greenнего есть дочерние элементы ). Изнутри обработчик, вы должны использовать thisссылку. Смотри эту скрипку .
LeGEC
1
Еще одна заметка, добавляемая к теме делегирования - это и очень полезно, и очень эффективно. Вы будете использовать меньше ресурсов браузера с делегированием против присоединения события к каждому соответствующему элементу. Думаю, я бы упомянул об этом, на случай, если людям понадобится больше причин использовать делегирование.
phatskat
6

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

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

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

NNNNNN
источник
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накладных расходов, но не цитируйте меня об этом.
Cowbert
2

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

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

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

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

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

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

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

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

Контрольный список того, почему делегирование .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/

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

Мацей Сикора
источник
если подключено много обработчиков событий (например, каждой строки таблицы), часто лучше по производительности использовать один делегированный обработчик для контейнера вместо присоединения многих прямых обработчиков. Это видно из того факта, что количество обработчиков событий само по себе является метрикой профилирования.
Cowbert