Прямое или делегированное - jQuery .on()
Я пытаюсь понять эту особую разницу между прямым и делегированным обработчиками событий с помощью метода 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");
});
Может быть, кто-то мог бы обратиться к другому примеру, чтобы прояснить этот момент? Благодарю.
6 ответов
Случай 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 только контейнер был дан инструкции; он отвечает за уведомление о кликах от имени своих дочерних элементов. Работа по отлову событий была делегирована. Это также означает, что инструкция будет выполняться для дочерних элементов, которые будут созданы в будущем.
Первый способ, $("div#target span.green").on()
, привязывает обработчик щелчка непосредственно к диапазону (ам), который соответствует селектору в момент выполнения кода. Это означает, что если другие диапазоны будут добавлены позже (или их класс будет изменен для соответствия), они пропустят и не будут иметь обработчика щелчков. Это также означает, что если впоследствии вы удалите "зеленый" класс из одного из диапазонов, его обработчик кликов будет продолжать работать - jQuery не отслеживает, как был назначен обработчик, и проверяет, соответствует ли селектор по-прежнему.
Второй способ, $("div#target").on()
, привязывает обработчик щелчка к совпадающим элементам (ам), которые совпадают (опять же, с теми, которые совпадают в тот момент), но когда щелчок происходит где-то в элементе div, функция обработчика будет выполняться, только если щелчок произошел не только в div, но в дочернем элементе, соответствующем селектору во втором параметре .on()
"span.green". Сделано так, что не имеет значения, когда были созданы эти дочерние промежутки, щелчок по ним все равно запускает обработчик.
Поэтому для страницы, которая динамически не добавляет и не изменяет свое содержимое, вы не заметите разницы между этими двумя методами. Если вы динамически добавляете дополнительные дочерние элементы, второй синтаксис означает, что вам не нужно беспокоиться о назначении им обработчиков кликов, потому что вы уже сделали это один раз для родительского элемента.
Объяснение N3dst4 идеально. Исходя из этого, мы можем предположить, что все дочерние элементы находятся внутри тела, поэтому нам нужно использовать только это:
$('body').on('click', '.element', function(){
alert('It works!')
});
Работает с прямым или делегированным событием.
Касательно OP, но концепция, которая помогла мне распутать путаницу с этой функцией, заключается в том, что связанные элементы должны быть родителями выбранных элементов.
- Связанный относится к тому, что осталось от
.on
, - Выбранный относится ко 2-му аргументу
.on()
,
Делегирование не работает как.find(), выбирая подмножество связанных элементов. Селектор применяется только к строгим дочерним элементам.
$("span.green").on("click", ...
сильно отличается от
$("span").on("click", ".green", ...
В частности, чтобы получить преимущества, на которые намекает @N3dst4 с "элементами, которые будут созданы в будущем", связанный элемент должен быть постоянным родителем. Тогда выбранные дети могут приходить и уходить.
РЕДАКТИРОВАТЬ
Контрольный список того, почему делегированы .on
не работает
Хитрые причины, почему $('.bound').on('event', '.selected', some_function)
может не работать:
- Связанный элемент не является постоянным. Было создано после звонка
.on()
- Выбранный элемент не является надлежащим потомком связанного элемента. Это тот же элемент.
- Выбранный элемент предотвратил всплытие события в связанный элемент, вызвав
.stopPropagation()
,
(Опуская менее хитрые причины, такие как селектор с ошибкой.)
Я написал пост со сравнением прямых и делегированных событий. Я сравниваю чистый js, но он имеет то же значение для jquery, которое только инкапсулирует его.
Вывод заключается в том, что делегированная обработка событий предназначена для динамической структуры DOM, в которой можно создавать связанные элементы, пока пользователь взаимодействует со страницей (повторные привязки не нужны), а прямая обработка событий - для статических элементов DOM, когда мы знаем, что структура не изменится.
Для получения дополнительной информации и полного сравнения - http://maciejsikora.com/standard-events-vs-event-delegation/
Использование всегда делегированных обработчиков, что, на мой взгляд, является очень модным в настоящее время, является неправильным способом, многие программисты используют его, потому что "его следует использовать", но правда в том, что прямые обработчики событий лучше в некоторых ситуациях, и выбор, какой метод использовать, должен поддерживаться. знанием различий.
Случай 3 (делегированный):
$("div#target").delegate("span.green", "click", function() {...});