Привязка событий к динамически создаваемым элементам?

У меня есть небольшой код, в котором я просматриваю все поля выбора на странице и привязываю к ним событие .hover, чтобы немного поиграть с их шириной на mouse on/off.

Это происходит на готовой странице и работает нормально.

У меня проблема в том, что любые поля выбора, которые я добавляю через Ajax или DOM после начального цикла, не имеют привязки к событию.

Я нашел этот плагин (Плагин jQuery Live Query), но прежде чем добавить еще 5k на свои страницы с помощью плагина, я хочу узнать, знает ли кто-нибудь способ сделать это напрямую с помощью jQuery или другим способом.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1 677
0
916 683
23
Перейти к ответу Данный вопрос помечен как решенный

Ответы 23

Вы можете добавлять события к объектам при их создании. Если вы добавляете одни и те же события к нескольким объектам в разное время, создание именованной функции может оказаться подходящим вариантом.

var mouseOverHandler = function() {
    // Do stuff
};
var mouseOutHandler = function () {
    // Do stuff
};

$(function() {
    // On the document load, apply to existing elements
    $('select').hover(mouseOverHandler, mouseOutHandler);
});

// This next part would be in the callback from your Ajax call
$("<select></select>")
    .append( /* Your <option>s */ )
    .hover(mouseOverHandler, mouseOutHandler)
    .appendTo( /* Wherever you need the select box */ )
;

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

function addCallbacks(eles){
    eles.hover(function(){alert("gotcha!")});
}

$(document).ready(function(){
    addCallbacks($(".myEles"))
});

// ... add elements ...
addCallbacks($(".myNewElements"))

Этот пост действительно помог мне разобраться в проблеме, с которой я загружал ту же форму и получал 1,2,4,8,16 ... отправлений. Вместо использования .live () я просто использовал .bind () в моем обратном вызове .load (). Проблема решена. Спасибо!

theflowersoftime 24.08.2011 13:24
Ответ принят как подходящий

Начиная с jQuery 1.7 вы должны использовать jQuery.fn.on с заполненным параметром селектора:

$(staticAncestors).on(eventName, dynamicChild, function() {});

Объяснение:

Это называется делегированием событий и работает следующим образом. Событие прикрепляется к статическому родительскому элементу (staticAncestors) элемента, который должен быть обработан. Этот обработчик jQuery запускается каждый раз, когда событие запускается для этого элемента или одного из дочерних элементов. Затем обработчик проверяет, соответствует ли элемент, вызвавший событие, вашему селектору (dynamicChild). Когда есть совпадение, выполняется ваша пользовательская функция обработчика.


Перед этим, рекомендуемый подход заключался в использовании live():

$(selector).live( eventName, function(){} );

Однако live() был объявлен устаревшим в версии 1.7 в пользу on() и полностью удален в версии 1.9. Подпись live():

$(selector).live( eventName, function(){} );

... можно заменить следующей подписью on():

$(document).on( eventName, selector, function(){} );

Например, если ваша страница динамически создавала элементы с именем класса dosomething, вы должны привязать событие к родитель, который уже существует (это суть проблемы здесь, вам нужно что-то, что существует для привязки, не привязывайтесь к динамическому контенту) , это может быть (и самый простой вариант) document. Хотя имейте в виду document может быть не самым эффективным вариантом.

$(document).on('mouseover mouseout', '.dosomething', function(){
    // what you want to happen when mouseover and mouseout 
    // occurs on elements that match '.dosomething'
});

Любой родительский элемент, существующий на момент привязки события, в порядке. Например

$('.buttons').on('click', 'button', function(){
    // do something here
});

будет применяться к

<div class = "buttons">
    <!-- <button>s that are generated dynamically and added here -->
</div>

Обратите внимание, что метод live работает только для определенных событий, но не для других, таких как loadedmetadata. (См. Раздел «Предупреждения» в документации.)

Sam Dutton 17.02.2011 14:47

Узнайте больше о делегировании событий здесь: learn.jquery.com/events/event-delegation.

Felix Kling 07.06.2013 15:21

Любой способ сделать это с помощью чистого javascript / vanilla js?

Ram Patra 12.11.2014 15:33

@Ramswaroop Все, что вы можете сделать в jQuery, можно сделать без jQuery. Вот хороший пример делегирования событий без jQuery

Dave 08.12.2014 20:46

@dave Интересно, почему здесь нет указанного вами ответа. Эли явно попросил решение, если возможно, без каких-либо плагинов.

Ram Patra 09.12.2014 10:14

До jq1.7 и для jq1.4.2> рекомендованным методом было использование delegate(): api.jquery.com/delegate (который, кстати, не удаляется из jq) As of jQuery 1.7, .delegate() has been superseded by the .on() method. For earlier versions, however, it remains the most effective means to use event delegation

A. Wolff 04.02.2015 17:29

Обратите внимание, что некоторые события не распространяются, поэтому их нельзя использовать для делегирования событий, например error - см .: stackoverflow.com/a/18683695/2181514

freedomn-m 25.09.2020 13:26

Попробуйте использовать .live() вместо .bind(); .live() свяжет .hover с вашим флажком после выполнения запроса Ajax.

Метод live() объявлен устаревшим в версии 1.7 в пользу on и удален в версии 1.9.

chridam 17.06.2014 16:30

Вы можете использовать метод live () для привязки элементов (даже вновь созданных) к событиям и обработчикам, например к событию onclick.

Вот пример кода, который я написал, где вы можете увидеть, как метод live () связывает выбранные элементы, даже недавно созданные, с событиями:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns = "http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" />
        <title>Untitled Document</title>
    </head>

    <body>
        <script src = "http://code.jquery.com/jquery-latest.js"></script>
        <script src = "http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.16/jquery-ui.min.js"></script>

        <input type = "button" id = "theButton" value = "Click" />
        <script type = "text/javascript">
            $(document).ready(function()
                {
                    $('.FOO').live("click", function (){alert("It Works!")});
                    var $dialog = $('<div></div>').html('<div id = "container"><input type  = "button" id = "CUSTOM" value = "click"/>This dialog will show every time!</div>').dialog({
                                                                                                         autoOpen: false,
                                                                                                         tite: 'Basic Dialog'
                                                                                                     });
                    $('#theButton').click(function()
                    {
                        $dialog.dialog('open');
                        return('false');
                    });
                    $('#CUSTOM').click(function(){
                        //$('#container').append('<input type = "button" value = "clickmee" class = "FOO" /></br>');
                        var button = document.createElement("input");
                        button.setAttribute('class','FOO');
                        button.setAttribute('type','button');
                        button.setAttribute('value','CLICKMEE');
                        $('#container').append(button);
                    });
                    /* $('#FOO').click(function(){
                                                     alert("It Works!");
                                                 }); */
            });
        </script>
    </body>
</html>

В документации jQuery.fn.on есть хорошее объяснение.

Суммируя:

Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to .on().

Таким образом, в следующем примере #dataTable tbody tr должен существовать до создания кода.

$("#dataTable tbody tr").on("click", function(event){
    console.info($(this).text());
});

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

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

$("#dataTable tbody").on("click", "tr", function(event){
    console.info($(this).text());
});

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

Подход с делегированными событиями (второй пример кода) присоединяет обработчик событий только к одному элементу, tbody, и событию нужно только подняться на один уровень (от tr до tbody).

Примечание: Делегированные события не работают для SVG.

это был бы более приемлемый ответ, потому что было бы быстрее делегировать из конкретной таблицы, а не полностью из документа (область поиска была бы намного меньше)

msanjay 09.05.2014 22:14

@msanjay: Хотя нацеливание поиска ближе к элементам предпочтительнее, разница между поиском и скоростью на практике очень незначительна. Вам нужно будет щелкать 50 000 раз в секунду, чтобы что-то заметить :)

Gone Coding 12.11.2014 15:06

Спасибо! Это решит мою проблему. Это простое утверждение было моей проблемой: «Обработчики событий привязаны только к выбранным в данный момент элементам; они должны существовать на странице в то время, когда ваш код вызывает ...»

Dennis Fazekas 04.10.2019 00:31

Это решение чистый JavaScript без каких-либо библиотек или плагинов:

document.addEventListener('click', function (e) {
    if (hasClass(e.target, 'bu')) {
        // .bu clicked
        // Do your thing
    } else if (hasClass(e.target, 'test')) {
        // .test clicked
        // Do your other thing
    }
}, false);

где hasClass - это

function hasClass(elem, className) {
    return elem.className.split(' ').indexOf(className) > -1;
}

Live demo

Кредит принадлежит Дэйву и Симе Видасу.

Используя более современный JS, hasClass может быть реализован как:

function hasClass(elem, className) {
    return elem.classList.contains(className);
}
stackoverflow.com/questions/9106329/…
zloctb 06.01.2016 17:57

Вы можете использовать Element.classList вместо разделения

Eugen Konkov 09.08.2016 13:07

@EugenKonkov Element.classList не поддерживается в старых браузерах. Например, IE <9.

Ram Patra 09.08.2016 13:59

Хорошая статья о том, как добиться цели, используя ванильный скрипт вместо jQuery - toddmotto.com/…

Ram Patra 29.06.2017 12:25

как насчет пузырей? Что, если событие щелчка произошло на дочернем элементе интересующего вас элемента?

Andreas Trantidis 06.07.2017 16:06

@AndreasTrantid - вам нужно проверить класс дочернего элемента: jsfiddle.net/ramswaroop/d8e1860r

Ram Patra 06.07.2017 16:19

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

oldboy 16.10.2020 14:21

Другое решение - добавить слушателя при создании элемента. Вместо того, чтобы помещать слушателя в тело, вы помещаете слушателя в элемент в тот момент, когда вы его создаете:

var myElement = $('<button/>', {
    text: 'Go to Google!'
});

myElement.bind( 'click', goToGoogle);
myElement.append('body');


function goToGoogle(event){
    window.location.replace("http://www.google.com");
}

Ваш код содержит 1 ошибку: myElement.append('body'); должен быть myElement.appendTo('body');. С другой стороны, если нет необходимости в дальнейшем использовании переменной myElement, это проще и короче: $('body').append($('<button/>', { text: 'Go to Google!' }).bind( 'click', goToGoogle));

ddlab 11.05.2017 22:58

Я предпочитаю использовать селектор и применяю его к документу.

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

Например:

$(document).on("click", 'selector', function() {
    // Your code here
});

селектор не должен быть заключен в $, поэтому правильный формат будет $ (document) .on ("click", "selector", function () {// Ваш код здесь});

autopilot 09.04.2018 07:21

Также бессмысленно оборачивать объект jQuery вокруг переменной selector, когда он должен содержать либо строку, либо объект Element, который вы можете просто передать непосредственно этому аргументу on().

Rory McCrossan 20.06.2018 17:57

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

Mark Baijens 21.09.2020 14:28

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

$(document).ready(function(){
  //Particular Parent chield click
  $(".buttons").on("click","button",function(){
    alert("Clicked");
  });  
  
  //Dynamic event bind on button class  
  $(document).on("click",".button",function(){
    alert("Dymamic Clicked");
  });
  $("input").addClass("button");  
});
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class = "buttons">
  <input type = "button" value = "1">
  <button>2</button>
  <input type = "text">
  <button>3</button>  
  <input type = "button" value = "5">  
  </div>
<button>6</button>

Привязка событий к динамически создаваемым элементам

Одиночный элемент:

$(document.body).on('click','.element', function(e) {  });

Дочерний элемент:

 $(document.body).on('click','.element *', function(e) {  });

Обратите внимание на добавленный *. Событие будет инициировано для всех дочерних элементов этого элемента.

Я заметил, что:

$(document.body).on('click','.#element_id > element', function(e) {  });

Он больше не работает, но работал раньше. Я использовал jQuery от Google CDN, но не знаю, изменили ли они его.

Да, и они не говорят (document.body), что это говорит предок, который может быть чем угодно

MadeInDreams 23.01.2016 19:29

Обратите внимание на класс "MAIN", в котором размещается элемент, например,

<div class = "container">
     <ul class = "select">
         <li> First</li>
         <li>Second</li>
    </ul>
</div>

В приведенном выше сценарии основным объектом, за которым будет следить jQuery, является «контейнер».

Тогда у вас будут в основном имена элементов в контейнере, такие как ul, li и select:

$(document).ready(function(e) {
    $('.container').on( 'click',".select", function(e) {
        alert("CLICKED");
    });
 });

Попробуйте так -

$(document).on( 'click', '.click-activity', function () { ... });

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

Mark Baijens 21.09.2020 14:28

вы могли бы использовать

$('.buttons').on('click', 'button', function(){
    // your magic goes here
});

или же

$('.buttons').delegate('button', 'click', function() {
    // your magic goes here
});

эти два метода эквивалентны, но имеют разный порядок параметров.

см .: Событие делегата jQuery

delegate() устарел. Не используйте это.

Rory McCrossan 20.06.2018 17:58

Используйте метод .on() в jQuery http://api.jquery.com/on/, чтобы прикрепить обработчики событий к живому элементу.

Также с версии 1.9 метод .live() удален.

Вы можете прикрепить событие к элементу при динамическом создании с помощью jQuery(html, attributes).

As of jQuery 1.8, any jQuery instance method (a method of jQuery.fn) can be used as a property of the object passed to the second parameter:

function handleDynamicElementEvent(event) {
  console.info(event.type, this.value)
}
// create and attach event to dynamic element
jQuery("<select>", {
    html: $.map(Array(3), function(_, index) {
      return new Option(index, index)
    }),
    on: {
      change: handleDynamicElementEvent
    }
  })
  .appendTo("body");
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>

Еще одно гибкое решение для создания элементов и привязки событий (источник)

// creating a dynamic element (container div)
var $div = $("<div>", {id: 'myid1', class: 'myclass'});

//creating a dynamic button
 var $btn = $("<button>", { type: 'button', text: 'Click me', class: 'btn' });

// binding the event
 $btn.click(function () { //for mouseover--> $btn.on('mouseover', function () {
    console.info('clicked');
 });

// append dynamic button to the dynamic container
$div.append($btn);

// add the dynamically created element(s) to a static element
$("#box").append($div);

Примечание: Это создаст экземпляр обработчика событий для каждого элемента. (может повлиять на производительность при использовании в циклах)

Вот почему динамически созданные элементы не реагируют на щелчки :

var body = $("body");
var btns = $("button");
var btnB = $("<button>B</button>");
// `<button>B</button>` is not yet in the document.
// Thus, `$("button")` gives `[<button>A</button>]`.
// Only `<button>A</button>` gets a click listener.
btns.on("click", function () {
  console.info(this);
});
// Too late for `<button>B</button>`...
body.append(btnB);
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>

В качестве обходного пути вы должны прослушать все щелчки и проверить исходный элемент  :

var body = $("body");
var btnB = $("<button>B</button>");
var btnC = $("<button>C</button>");
// Listen to all clicks and
// check if the source element
// is a `<button></button>`.
body.on("click", function (ev) {
  if ($(ev.target).is("button")) {
    console.info(ev.target);
  }
});
// Now you can add any number
// of `<button></button>`.
body.append(btnB);
body.append(btnC);
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>

Это называется «Делегирование событий». Хорошие новости, это встроенная функция jQuery :-)

var i = 11;
var body = $("body");
body.on("click", "button", function () {
  var letter = (i++).toString(36).toUpperCase();
  body.append($("<button>" + letter + "</button>"));
});
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>

Также возможно назначить var body = $("body").on(); напрямую.

Sebastian Simon 26.07.2020 03:18

<html>
    <head>
        <title>HTML Document</title>
        <script src = "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
    </head>

    <body>
        <div id = "hover-id">
            Hello World
        </div>

        <script>
            jQuery(document).ready(function($){
                $(document).on('mouseover', '#hover-id', function(){
                    $(this).css('color','yellowgreen');
                });

                $(document).on('mouseout', '#hover-id', function(){
                    $(this).css('color','black');
                });
            });
        </script>
    </body>
</html>

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

Palec 30.09.2017 14:07

Я искал решение, чтобы $.bind и $.unbind работали без проблем в динамически добавленных элементах.

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

const sendAction = function(e){ ... }
// bind the click
$('body').on('click', 'button.send', sendAction );

// unbind the click
$('body').on('click', 'button.send', function(){} );

Отмена привязки не работает, это просто добавляет еще одно событие, которое указывает на пустую функцию ...

Fabian Bigler 09.11.2018 14:28

Свяжите событие с уже существующим родителем:

$(document).on("click", "selector", function() {
    // Your code here
});

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

var iterations = 4;
var button;
var body = document.querySelector("body");

for (var i = 0; i < iterations; i++) {
    button = document.createElement("button");
    button.classList.add("my-button");
    button.appendChild(document.createTextNode(i));
    button.addEventListener("click", myButtonWasClicked);
    body.appendChild(button);
}

function myButtonWasClicked(e) {
    console.info(e.target); //access to this specific button
}

Я предпочитаю эту реализацию; Мне просто нужно настроить обратный звонок

William 22.10.2018 13:18

Это делается делегированием события. Событие будет привязано к элементу класса-оболочки, но будет делегировано элементу класса-селектора. Вот как это работает.

$('.wrapper-class').on("click", '.selector-class', function() {
    // Your code here
});

И HTML

<div class = "wrapper-class">
    <button class = "selector-class">
      Click Me!
    </button>
</div>    

#Примечание: Элемент класса-оболочки может быть любым, например. документ, тело или ваша обертка. Обертка уже должна существовать. Однако selector не обязательно должен быть представлен во время загрузки страницы. Это может произойти позже, и событие будет привязано к selectorбезошибочно.

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

Asad Ali 05.08.2020 19:36

Если вы привязываете одно и то же событие к родительскому и дочернему элементу, вы можете остановить его с помощью event.stopPropagation ()

Mustkeem K 06.08.2020 10:18

Другие вопросы по теме