jQuery .live () и метод .on () для добавления события щелчка после загрузки динамического HTML

217

Я использую jQuery v.1.7.1, где метод .live () явно устарел.

Проблема у меня заключается в том, что при динамической загрузке HTML в элемент с помощью:

$('#parent').load("http://..."); 

Если я попытаюсь добавить событие click после этого, оно не зарегистрирует событие одним из следующих способов:

$('#parent').click(function() ...); 

или

// according to documentation this should be used instead of .live()
$('#child').on('click', function() ...); 

Как правильно достичь этой функциональности? Кажется, он работает только с .live (), но я не должен использовать этот метод. Обратите внимание, что #child является динамически загружаемым элементом.

Спасибо.

Шон Томан
источник
25
Почему вы говорите «якобы устарел» ? Вы не верите документам?
6
Это не якобы устарел: он является устаревшим. Если вы посмотрите на документацию jQuery,.live() она расскажет вам, как переписать существующие варианты .live()использования .delegate()или .on()(в зависимости от того, используете ли вы версию 1.7+ или нет). Обратите внимание, что если вы добавите обработчик с .click()«after», как вы упомянули, т. Е. После динамической загрузки элементов, он должен работать - единственная проблема - попытка назначить его .click() перед динамической загрузкой элементов.
nnnnnn
Я изменил формулировку на «очевидно», так как это в основном то, что я имел в виду. В любом случае, теперь я понимаю, что, очевидно, поскольку событие .load () является асинхронным, тогда элемент #child может быть надежно распознан только в обработчике успеха, что имеет смысл.
Шон Томан

Ответы:

607

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

$('#parent').on("click", "#child", function() {});

Обработчик события будет прикреплен к #parentобъекту, и каждый раз #child, когда возникнет событие щелчка, возникшее на нем, будет запущен ваш обработчик щелчка. Это называется делегированной обработкой событий (обработка событий делегируется родительскому объекту).

Это сделано так, потому что вы можете прикрепить событие к #parentобъекту, даже если #childобъект еще не существует, но когда он позже существует и щелкается, событие click будет пузыриться до #parentобъекта, оно увидит, что оно возникло, #childи есть обработчик события для клика #childи запуска вашего события.

jfriend00
источник
23
Отличное объяснение! Я никогда прежде не мог обернуться live()против, on()но сегодня вечером я решил еще раз попробовать, и ваше объяснение сразу показало, чего мне не хватало с самого начала. Спасибо!
MikeSchinkel
6
Миллион взлетов . Я начал использовать нокаут до того, как понял, что это возможно в jQuery для динамически создаваемых детей, большое спасибо.
Брок Хенсли
9
Вы должны написать книгу или что-то в этом роде: ваш маленький текст был для меня более полезным, чем полная страница в документации по jQuery « on () ». Большое спасибо!
Холестерин
@ jfriend00, знаете ли вы, как мы можем применить этот же процесс к зависанию, а не к зависанию? Есть ли источник, который говорит об этом?
Klewis
@blackhawk - Смотрите этот ответ . Вы можете зарегистрироваться для mouseenterи mouseleaveсобытий и испытаний внутри обработчика , который один был вызван. Псевдо-событие hover в jQuery недоступно с делегированием.
jfriend00
33

Попробуй это:

$('#parent').on('click', '#child', function() {
    // Code
});

Из $.on()документации:

Обработчики событий связаны только с выбранными в данный момент элементами; они должны существовать на странице во время вызова вашего кода .on().

Ваш #childэлемент не существует, когда вы вызываете $.on()его, поэтому событие не связано (в отличие от $.live()). #parentОднако, действительно существует, поэтому связывание события , что это хорошо.

Второй аргумент в моем коде выше , действуют как «фильтр» срабатывают только в случае , если событие ого до #parentс #child.

Bojangles
источник
Если я вызываю метод $ .on () после метода $ .load (), то почему в этой точке не существует элемент #child?
Шон Томан
6
@SeanThoman - вы должны вызывать его из обработчика успеха .load()метода, а не из кода после .load()метода. #childизвестно, что он загружается только тогда, когда обработчик успеха выполняется, а не раньше.
jfriend00
и даже тогда время, необходимое для вставки данных, которые вы вернули в DOM, означает, что они могут не подключаться. В последнее время я много работаю с одностраничными приложениями, и моим стандартом является var $ body = $ ('body'); $ body.on ("event", ".element / # class", function (e) {}); За все. 1 селектор. Не беспокойтесь о том, что загружено или нет.
2013 года
21

$(document).on('click', '.selector', function() { /* do stuff */ });

РЕДАКТИРОВАТЬ: Я предоставляю немного больше информации о том, как это работает, потому что ... слова. В этом примере вы размещаете слушателя на весь документ.

Когда вы подходите к clickлюбому элементу (элементам) .selector, событие всплывает до основного документа - при условии, что нет других слушателей, вызывающих event.stopPropagation()метод - что приведет к нарастанию пузыря события для родительских элементов.

Вместо привязки к определенному элементу или набору элементов вы прослушиваете любые события, поступающие от элементов, которые соответствуют указанному селектору. Это означает, что вы можете создать один прослушиватель, один раз, который будет автоматически сопоставлять существующие в настоящее время элементы, а также любые динамически добавленные элементы.

Это разумно по нескольким причинам, включая производительность и использование памяти (в крупномасштабных приложениях)

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

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

L422Y
источник
23
Он пытался $(element).on(...). Это $(document).on(...,element,...). Разные звери.
Цао
@ArnoldRoa да, лучшая производительность. у вас не будет прослушивателя на нескольких дочерних элементах, вам не придется повторно применять прослушиватель каждый раз, когда DOM изменяется, и события по умолчанию всплывают через DOM. Запускается один раз, и каждый дочерний элемент, соответствующий селектору, при нажатии вызывает данную функцию.
L422Y
@ L422Y комментарий здесь вводит в заблуждение. Если вам интересно о последствиях производительности, обратите внимание на @ jfriend00 свой комментарий на @ ответ Джареда
Gust ван де Wal
@GustvandeWal Как это вводит в заблуждение? Если вы идете и запускаете сто долларов (this) .on (...) против прослушивания события один раз на родительском элементе, это существенно более эффективно.
L422Y
Вы говорите только о прикреплении событий. Реальные узкие места производительности - слишком много слушателей, запускаемых (как те, которые вы делегируете), а не большое количество элементов, которым нужно событие, прикрепленное к ним.
Гаст ван де Валь
12

Эквивалент .live () в 1.7 выглядит так:

$(document).on('click', '#child', function() ...); 

По сути, следите за документом по событиям кликов и фильтруйте их по #child.

Джаред
источник
Потому что именно так обработчик событий присоединяется к документу (как это .live()делается).
Цао
17
Одной из причин, по которой .live()это устарело, является то, что все объекты обработчиков событий в объекте документа плохо размещать. Вещи могут действительно, действительно замедляться. Мало того, что события должны пузыриться до самого документа, у вас может быть много обработчиков событий для просмотра объекта документа. Основным преимуществом .on()является то, что вы можете прикрепить его к родительскому объекту, который намного ближе к реальному объекту и значительно повысить производительность. Итак ... Я бы не рекомендовал использовать .on()с объектом документа. Намного лучше выбрать более близкий родительский объект.
jfriend00
Спасибо за разъяснения. Что бы это ни стоило, производительность событий была значительно улучшена с помощью on (), поэтому это должно быть меньшей проблемой, чем раньше. При подключении к родителю, я бы предположил, что это должен быть родитель, который существует в готовом документе, или вы увидите похожие проблемы.
Джаред
В худшем случае, это делает эмулировать устаревший .live()подход; однако способность контролировать делегирование, безусловно, выгодна.
Дэн Лугг
9

Я знаю, что уже немного поздно для ответа, но я создал polyfill для метода .live (). Я протестировал его в jQuery 1.11, и, похоже, он работает довольно хорошо. Я знаю, что мы должны реализовывать метод .on () везде, где это возможно, но в больших проектах, где невозможно преобразовать все вызовы .live () в эквивалентные вызовы .on () по любой причине, может произойти следующее работай:

if(jQuery && !jQuery.fn.live) {
    jQuery.fn.live = function(evt, func) {
        $('body').on(evt, this.selector, func);
    }
}

Просто включите его после загрузки jQuery и перед вызовом live ().

NSV
источник
5

.on () для jQuery версии 1.7 и выше. Если у вас более старая версия, используйте это:

$("#SomeId").live("click",function(){
    //do stuff;
});
Мэтт Кашатт
источник
8
ОП говорит: «Я использую jQuery v.1.7.1»
Selvakumar Arumugam
1
@ jfriend - почему бы не опубликовать свой ответ вместо того, чтобы понизить мой? Я использую live () каждый божий день со всеми версиями младше 1.6, и он работает нормально. Я вижу, что вы хорошо читаете документацию, но иногда это больше помогает реализовать что-то на практике для человека. .live () работает. Период.
Мэтт Кашатт
5
@MatthewPatrickCashatt - я опубликовал свой собственный ответ и не отрицал ваш - не делайте таких слепых предположений. До 1.7 .delegate()работает намного лучше, чем .live()поэтому jQuery doc рекомендует это и не одобряет .live(). Да, .live()все еще работает, но ответы здесь на SO должны рекомендовать лучший способ делать вещи. Я видел это 20 раз здесь, на SO. Если вы разместите .live()здесь код на SO, он получит отрицательный ответ (как правило, не от меня, если с ним что-то серьезно не так).
jfriend00
1
Я не понизил голос, но хотя .live()определенно работает даже в версии 1.7, вы все равно должны использовать .delegate()или .on()потому что .live()устарел по уважительной причине. Если вы заметили, что изменение синтаксиса для двух новых функций будет работать нормально.
nnnnnn
1
@ jfriend00- Вы абсолютно правы. Долгий день. Мои извинения.
Мэтт Кашатт
3

Я использовал «live» в своем проекте, но один из моих друзей предложил мне использовать «on» вместо live. И когда я попытался использовать это, я столкнулся с проблемой, как у вас.

На своих страницах я динамически создаю ряды таблиц кнопок и многое другое. но когда я использую магию исчезла.

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

Напишите свой код как:

function caller(){
    $('.ObjectYouWntToCall').on("click", function() {...magic...});
}

Позвонить звонящему (); после создания вашего объекта на странице, как это.

$('<dom class="ObjectYouWntToCall">bla... bla...<dom>').appendTo("#whereeveryouwant");
caller();

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

Coderx07
источник