Прив’язка подій до динамічно створених елементів?


1677

У мене є трохи коду, де я переглядаю всі вибрані поля на сторінці і прив'язую .hoverдо них подію, щоб зробити трохи подвійного змісту з їх шириною mouse on/off.

Це відбувається на сторінці готового і працює чудово.

Проблема в тому, що будь-які вікна вибору, які я додаю через Ajax або DOM після початкового циклу, подія не буде пов'язана.

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

Відповіді:


2249

Станом на jQuery 1.7 слід використовувати jQuery.fn.on:

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

До цього рекомендованим підходом було використання live():

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

Однак live()був on()знятий у 1,7 на користь та повністю вилучений у 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>

7
Зауважте, що живий метод працює лише для певних подій, а не для інших, таких як завантажені метадані. (Дивіться розділ застережень у документації.)
Сем Даттон

48
Дізнайтеся більше про делегацію подій тут: learn.jquery.com/events/event-delegation .
Фелікс Клінг

9
Будь-який спосіб досягти цього за допомогою чистого javascript / vanilla js?
Ram Patra

15
@Ramswaroop все, що можна зробити в jQuery, можна здійснити без jQuery. Ось хороший приклад делегації подій без jQuery
Дейв

5
@dave Цікаво, чому відповідь, яку ви вказали, не вказана тут. Елі чітко попросив рішення без будь-якого плагіна, якщо це можливо.
Рам Патра

377

Є гарне пояснення в документації jQuery.fn.on.

Коротко:

Обробники подій прив'язані лише до обраних на даний момент елементів; вони повинні існувати на сторінці в той момент, коли ваш код робить дзвінок .on().

Таким чином, у наведеному нижче прикладі #dataTable tbody trповинен існувати до генерування коду.

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

Якщо на сторінку вводиться новий HTML, бажано використовувати делеговані події для додавання обробника подій, як описано далі.

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

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

Окрім їхньої здатності керувати подіями на нащадкових елементах, які ще не створені, ще однією перевагою делегованих подій є їх потенціал для значно нижчих накладних витрат, коли необхідно контролювати багато елементів. У таблиці даних, що містить 1000 рядків tbody, перший приклад коду додає обробник до 1000 елементів.

Підхід із делегованими подіями (другий приклад коду) приєднує обробник подій лише до одного елемента tbody, а подія має підніматися лише на один рівень (з натиснутого trна tbody).

Примітка. Делеговані події не працюють для SVG .


11
це було б краще прийнятою відповіддю, оскільки було б швидше делегувати з конкретної таблиці, а не весь шлях від документа (область пошуку була б значно меншою)
msanjay

5
@msanjay: Хоча бажано орієнтуватися на пошук ближче до елементів, різниця в пошуку / швидкості на практиці дуже незначна. Вам доведеться натискати 50 000 разів на секунду, щоб щось помітити :)
Пройшло кодування

2
Чи можна застосувати цей підхід до обробки клацань прапорець підряд, змінивши trвідповідний селектор, наприклад 'input[type=checkbox]', і це буде автоматично оброблятися для нових вставлених рядків?
JoeBrockhaus

1
Дякую! Це вирішує мою проблему. Це просте твердження було моєю проблемою: "Обробники подій прив'язані лише до вибраних на даний момент елементів; вони повинні існувати на сторінці в той момент, коли ваш код робить дзвінок ..."
Dennis Fazekas

216

Це чисте рішення 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);
}


6
Ви можете використовувати Element.classList замість розділення
Євген Конков

2
@EugenKonkov Element.classListне підтримується у старих браузерах. Наприклад, IE <9.
Ram Patra

2
Приємна стаття про те, як зробити справи, використовуючи сценарій ванілі замість jQuery - toddmotto.com/…
Ram Patra

2
як щодо булькання? Що робити, якщо подія клацання сталася з дочірнім елементом, який вас цікавить?
Андреас Трантідідіс

73

Ви можете додавати події до об'єктів під час їх створення. Якщо ви додаєте одні й ті самі події до декількох об'єктів у різний час, може бути створено названу функцію.

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 */ )
;

49

Ви можете просто зав'язати свій виклик, який пов'язує події, у функцію, а потім двічі викликати його: один раз на документ готовий і один раз після події, що додає нові елементи DOM. Якщо ви зробите це, ви хочете уникнути прив'язки однієї і тієї ж події двічі до існуючих елементів, тож вам потрібно буде від’єднати існуючі події або (краще) прив’язати до новостворених елементів DOM. Код виглядатиме приблизно так:

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

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

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

2
Ця публікація дуже допомогла мені зрозуміти проблему, яку я завантажував у тій же формі та отримував 1,2,4,8,16 ... подання. Замість використання .live () я просто використав .bind () у своєму зворотному звороті .load (). Проблема вирішена. Дякую!
Томас МакКейб

42

Спробуйте використовувати .live()замість .bind(); .live()буде зв'язуватися .hoverз вашим прапорцем після запиту виконує Ajax.


32
Метод live()був застарілий у версії 1.7 на користь onта видалений у версії 1.9.
хродам

27

Прив’язка подій до динамічно створених елементів

Одиничний елемент:

$(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

25

Ви можете використовувати метод 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>

22

Я вважаю за краще використовувати селектор і застосовую його до документа.

Це прив'язується до документа і буде застосовано до елементів, які будуть надані після завантаження сторінки.

Наприклад:

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

2
селектор не повинен бути вкладений у $, тому правильним форматом буде $ (документ) .on ("клацання", "селектор", функція () {// Ваш код тут});
автопілот

1
Також безглуздо обгортати об’єкт jQuery навколо selectorзмінної, коли він повинен містити рядок або об'єкт Element, який ви можете просто передати безпосередньо цьому аргументуon()
Rory McCrossan

20

Ще одне рішення - додати слухача при створенні елемента. Замість того, щоб помістити слухача в тіло, ви поставите слухача в елемент в момент, коли ви його створите:

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


16

Візьміть до уваги клас "ОСНОВНИЙ" елемент, який розміщується, наприклад,

<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");
    });
 });

14

ви могли б використовувати

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

або

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

ці два методи еквівалентні, але мають різний порядок параметрів.

дивіться: jQuery Delegate Event


2
delegate()зараз застаріло. Не використовуйте.
Рорі Маккросан

14

Ви можете приєднати подію до елемента, коли динамічно створюється за допомогою jQuery(html, attributes).

Станом на jQuery 1.8 , будь-який метод екземпляра jQuery (метод jQuery.fn) може використовуватися як властивість об'єкта, переданого другому параметру:

function handleDynamicElementEvent(event) {
  console.log(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>


14

Це робиться делегацією заходу . Подія прив'язується до елемента класу обгортки, але буде делегована елементу класу selector. Ось як це працює.

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

Примітка:

Елементом класу обгортки може бути будь-що, наприклад, документ, корпус або обгортка. Обгортка повинна вже існувати . Однак, selectorце не обов'язково потрібно подавати під час завантаження сторінки. Він може настати пізніше, і подія прив’яжеться до selector ладу .


11

Ось чому динамічно створені елементи не реагують на кліки:

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.log(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.log(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>


10

Будь-який p arent, який існує на момент прив'язки події, і якщо ваша сторінка динамічно створювала елементи за допомогою кнопки імені класу, ви прив'язуєте подію до батька, який вже існує

$(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>


7

Прив’яжіть подію до батьків, який вже існує:

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


3

Я вважаю за краще, щоб слухачі подій розгорталися модульно, а не створювали сценарії 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.log(e.target); //access to this specific button
}


Я віддаю перевагу цій реалізації; Мені просто потрібно зателефонувати
Вільям

3

Ще одне гнучке рішення для створення елементів та прив’язки подій ( джерело )

// 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.log('clicked');
 });

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

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

Примітка. Це створить екземпляр обробника подій для кожного елемента (може вплинути на продуктивність при використанні в циклі)


0
<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>

3
Хоча цей фрагмент коду може вирішити проблему, він не пояснює, чому або як він відповідає на питання. Будь ласка, додайте пояснення до свого коду, оскільки це дійсно допомагає покращити якість вашої публікації. Пам’ятайте, що ви відповідаєте на запитання читачів у майбутньому, і ці люди можуть не знати причини вашої пропозиції щодо коду.
Палець

0

Я шукав рішення, як отримати $.bindта $.unbindпрацювати без проблем у динамічно доданих елементах.

Як on () робить трюк приєднувати події, щоб створити фальшиву розв’язку для тих, до яких я прийшов:

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

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

Розв’язування не працює, це просто додає ще одну подію, яка вказує на порожню функцію ...
Фабіан Біглер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.