Wiązanie zdarzeń na dynamicznie tworzonych elementach?


1677

Mam trochę kodu, w którym przeglądam wszystkie zaznaczone pola na stronie i .hoverwiążę z nimi zdarzenie, aby zrobić kręcenie z ich szerokością mouse on/off.

Dzieje się tak na stronie gotowej i działa dobrze.

Problem, który mam, polega na tym, że wszystkie pola wyboru dodawane przez Ajax lub DOM po początkowej pętli nie będą powiązane ze zdarzeniem.

Znalazłem tę wtyczkę ( jQuery Live Query Plugin ), ale zanim dodam kolejne 5k do moich stron za pomocą wtyczki, chcę sprawdzić, czy ktoś wie, jak to zrobić, albo z jQuery bezpośrednio, albo inną opcją.

Odpowiedzi:


2249

Począwszy od jQuery 1.7 powinieneś używać jQuery.fn.on:

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

Wcześniej zalecanym podejściem było użycie live():

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

Jednak live()było przestarzałe w 1,7 za on()i całkowicie usuwa 1,9. live()Podpis:

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

... można zastąpić następującym on()podpisem:

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

Na przykład, jeśli twoja strona dynamicznie tworzy elementy z nazwą klasy dosomething, powiążesz zdarzenie z rodzicem, który już istnieje (jest to sedno problemu tutaj, potrzebujesz czegoś, co istnieje, aby się z nim połączyć, nie łącz z zawartość dynamiczna), może to być (i najłatwiejsza opcja) document. Pamiętaj documentjednak, że może nie być najbardziej wydajną opcją .

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

Każdy rodzic, który istnieje w momencie powiązania zdarzenia, jest w porządku. Na przykład

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

dotyczyłby

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

7
Należy pamiętać, że metoda na żywo działa tylko w przypadku niektórych zdarzeń, a nie innych, takich jak loadmetadata. (Zobacz rozdział dotyczący zastrzeżeń w dokumentacji.)
Sam Dutton

48
Dowiedz się więcej o delegowaniu wydarzeń tutaj: learn.jquery.com/events/event-delegation .
Felix Kling

9
Jakiś sposób, aby to osiągnąć za pomocą czystego javascript / vanilla js?
Ram Patra,

15
@ Ramswaroop wszystko, co możesz zrobić w jQuery, można osiągnąć bez jQuery. Oto dobry przykład delegowania wydarzeń bez jQuery
Dave

5
@dave Zastanawiam się, dlaczego wskazanej przez ciebie odpowiedzi nie ma tutaj. Eli wyraźnie poprosił o rozwiązanie bez żadnej wtyczki, jeśli to możliwe.
Ram Patra,

377

Istnieje dobre wytłumaczenie w dokumentacji jQuery.fn.on.

W skrócie:

Procedury obsługi zdarzeń są powiązane tylko z aktualnie wybranymi elementami; muszą istnieć na stronie w momencie, gdy Twój kod wywołuje połączenie .on().

Dlatego w poniższym przykładzie #dataTable tbody trmusi istnieć przed wygenerowaniem kodu.

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

Jeśli do strony wstrzykuje się nowy kod HTML, lepiej jest użyć zdarzeń delegowanych w celu dołączenia procedury obsługi zdarzeń, jak opisano poniżej.

Zdarzenia delegowane mają tę zaletę, że mogą przetwarzać zdarzenia z elementów potomnych, które są dodawane do dokumentu w późniejszym czasie. Na przykład, jeśli tabela istnieje, ale wiersze są dodawane dynamicznie za pomocą kodu, następujące elementy ją obsłużą:

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

Oprócz zdolności do obsługi zdarzeń na elementach potomnych, które nie zostały jeszcze utworzone, kolejną zaletą zdarzeń delegowanych jest ich potencjał znacznie niższego obciążenia, gdy wiele elementów musi być monitorowanych. W tabeli danych zawierającej 1000 wierszy tbodypierwszy przykład kodu dołącza moduł obsługi do 1000 elementów.

Podejście z delegowanymi zdarzeniami (drugi przykład kodu) dołącza moduł obsługi zdarzenia do tylko jednego elementu tbody, a zdarzenie musi tylko przejść w górę o jeden poziom (od kliknięcia trdo tbody).

Uwaga: Zdarzenia delegowane nie działają w przypadku SVG .


11
to byłaby lepiej zaakceptowana odpowiedź, ponieważ szybciej byłoby delegować dane z określonej tabeli niż z samego dokumentu (obszar wyszukiwania byłby znacznie mniejszy)
msanjay

5
@ msanjay: Chociaż preferowane jest kierowanie wyszukiwania bliżej elementów, różnica wyszukiwania / prędkości jest w praktyce bardzo niewielka. Aby cokolwiek zauważyć, musiałbyś kliknąć 50 000 razy na sekundę :)
Gone Coding

2
Czy to podejście można zastosować do obsługi kliknięć pól wyboru w rzędzie, zmieniając trna odpowiedni selektor, na przykład 'input[type=checkbox]', i byłoby to automatycznie obsługiwane dla nowo wstawianych wierszy?
JoeBrockhaus,

1
Dziękuję Ci! To rozwiązuje mój problem. To proste stwierdzenie było moim problemem: „Procedury obsługi zdarzeń są powiązane tylko z aktualnie wybranymi elementami; muszą istnieć na stronie w momencie, gdy twój kod wywołuje ...”
Dennis Fazekas

216

To jest czyste rozwiązanie JavaScript bez bibliotek lub wtyczek:

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

gdzie hasClassjest

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

Live demo

Podziękowania należą się Dave'owi i Sime'owi Vidasowi

Za pomocą bardziej nowoczesnego JS hasClassmożna zaimplementować jako:

function hasClass(elem, className) {
    return elem.classList.contains(className);
}


6
Możesz użyć Element.classList zamiast dzielić
Eugen Konkov

2
@EugenKonkov Element.classListnie jest obsługiwany w starszych przeglądarkach. Na przykład IE <9.
Ram Patra

2
Fajny artykuł o tym, jak załatwić sprawę za pomocą skryptu waniliowego zamiast jQuery - toddmotto.com/...
Ram Patra

2
co powiesz na bulgotanie? Co się stanie, jeśli zdarzenie kliknięcia miało miejsce w elemencie potomnym elementu, który Cię interesuje?
Andreas Trantidis,

73

Możesz dodawać zdarzenia do obiektów podczas ich tworzenia. Jeśli dodajesz te same zdarzenia do wielu obiektów w różnych momentach, dobrym rozwiązaniem może być utworzenie nazwanej funkcji.

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

Możesz po prostu zawinąć wywołanie powiązania zdarzenia w funkcję, a następnie wywołać go dwukrotnie: raz w dokumencie gotowy i raz po zdarzeniu, które dodaje nowe elementy DOM. Jeśli to zrobisz, będziesz chciał uniknąć podwójnego wiązania tego samego zdarzenia z istniejącymi elementami, więc musisz usunąć powiązanie istniejących zdarzeń lub (lepiej) powiązać tylko z nowo utworzonymi elementami DOM. Kod wyglądałby mniej więcej tak:

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

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

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

2
Ten post naprawdę pomógł mi zrozumieć problem z ładowaniem tego samego formularza i otrzymywaniem 1,2,4,8,16 ... zgłoszeń. Zamiast używać .live () właśnie użyłem .bind () w moim wywołaniu zwrotnym .load (). Problem rozwiązany. Dzięki!
Thomas McCabe

42

Spróbuj użyć .live()zamiast .bind(); .live()zwiąże .hoverdo wyboru po zgłoszeniu wniosku sporządzi Ajax.


32
Metoda live()została wycofana w wersji 1.7 na korzyść oni usunięta w wersji 1.9.
Chridam

27

Wiązanie zdarzeń na dynamicznie tworzonych elementach

Pojedynczy element:

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

Element podrzędny:

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

Zwróć uwagę na dodane *. Wydarzenie zostanie uruchomione dla wszystkich dzieci tego elementu.

Zauważyłem to:

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

To już nie działa, ale działało wcześniej. Korzystam z jQuery z Google CDN , ale nie wiem, czy to zmienili.


Tak, a oni nie mówią (document.body), że mówi przodek, którym może być
właściwie

25

Możesz użyć metody live (), aby powiązać elementy (nawet te nowo utworzone) ze zdarzeniami i modułami obsługi, takimi jak zdarzenie onclick.

Oto przykładowy kod, który napisałem, gdzie możesz zobaczyć, w jaki sposób metoda live () wiąże wybrane elementy, nawet te nowo utworzone, ze zdarzeniami:

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

Wolę używać selektora i stosuję go na dokumencie.

To wiąże się z dokumentem i będzie miało zastosowanie do elementów, które będą renderowane po załadowaniu strony.

Na przykład:

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

2
selektor nie powinien być ujęty w $, więc poprawnym formatem będzie $ (dokument) .on („kliknij”, „selektor”, funkcja () {// Twój kod tutaj});
autopilot

1
Nie ma również sensu owijać obiektu jQuery wokół selectorzmiennej, gdy musi ona zawierać ciąg lub obiekt Element, który można po prostu przekazać bezpośrednio do argumentuon()
Rory

20

Innym rozwiązaniem jest dodanie detektora podczas tworzenia elementu. Zamiast wstawiać detektor do ciała, wstawiasz detektor do elementu w momencie jego tworzenia:

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

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


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

Twój kod zawiera 1 błąd: myElement.append('body');musi być myElement.appendTo('body');. Z drugiej strony, jeśli nie ma potrzeby dalszego stosowania zmiennej myElementjest to łatwiejsze i krótsze w ten sposób:$('body').append($('<button/>', { text: 'Go to Google!' }).bind( 'click', goToGoogle));
ddlab

17

Spróbuj w ten sposób -

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

16

Zwróć uwagę na klasę „MAIN”, w której element jest umieszczony, na przykład

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

W powyższym scenariuszu GŁÓWNY obiekt, który będzie oglądać jQuery, to „kontener”.

Wtedy w zasadzie mają elementy nazwy pod pojemnikiem, takich jak ul, lioraz select:

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

14

możesz użyć

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

lub

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

te dwie metody są równoważne, ale mają inną kolejność parametrów.

patrz: jQuery Delegate Event


2
delegate()jest teraz przestarzałe Nie używaj tego.
Rory McCrossan,

14

Możesz dołączyć zdarzenie do elementu, gdy jest tworzone dynamicznie za pomocą jQuery(html, attributes).

Począwszy od jQuery 1.8 , dowolna metoda instancji jQuery (metoda jQuery.fn) może być używana jako właściwość obiektu przekazanego do drugiego parametru:

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

Odbywa się to poprzez delegowanie wydarzeń . Zdarzenie zostanie powiązane z elementem klasy opakowania, ale zostanie delegowane do elementu klasy selektora. Tak to działa.

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

Uwaga:

elementem klasy otoki może być cokolwiek np. dokument, treść lub opakowanie. Opakowanie powinno już istnieć . Jednak selectorniekoniecznie musi być prezentowane podczas ładowania strony. Może przyjść później, a wydarzenie będzie się wiązało selector bezbłędnie .


11

Oto dlaczego dynamicznie tworzone elementy nie reagują na kliknięcia:

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>

Aby obejść ten problem, należy odsłuchać wszystkie kliknięcia i sprawdzić element źródłowy:

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>

Nazywa się to „delegowaniem zdarzeń”. Dobra wiadomość, jest to wbudowana funkcja w 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

Wszelkie strony, które istnieją w momencie powiązania zdarzenia, a jeśli twoja strona dynamicznie tworzy elementy za pomocą przycisku nazwy klasy , powiążesz zdarzenie z rodzicem, który już istnieje

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

Powiąż zdarzenie z rodzicem, który już istnieje:

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


3

Wolę mieć detektory zdarzeń wdrożone w sposób modułowy niż skrypty documentdetektora zdarzeń poziomu. Lubię to poniżej. Uwaga: nie można nadpisać elementu z tym samym detektorem zdarzeń, więc nie martw się o dołączenie detektora więcej niż jeden raz - tylko jeden drążek.

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
}


Wolę tę implementację; Muszę tylko oddzwonić
William,

3

Kolejne elastyczne rozwiązanie do tworzenia elementów i łączenia zdarzeń ( źródło )

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

Uwaga: Spowoduje to utworzenie instancji procedury obsługi zdarzeń dla każdego elementu (może mieć wpływ na wydajność, gdy jest używany w pętlach)


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
Ten fragment kodu może rozwiązać problem, ale nie wyjaśnia, dlaczego ani w jaki sposób odpowiada na pytanie. Dołącz wyjaśnienie swojego kodu, ponieważ to naprawdę pomaga poprawić jakość Twojego postu. Pamiętaj, że w przyszłości odpowiadasz na pytanie czytelników, a ci ludzie mogą nie znać przyczyn Twojej sugestii kodu.
Palec

0

Szukałem rozwiązania, aby uzyskać $.bindi $.unbindpracować bez problemów w dynamicznie dodawanych elementach.

Jak na () robi sztuczkę, aby dołączyć zdarzenia, aby stworzyć fałszywe unbind na tych, do których przyszedłem:

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

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

Rozpakowywanie nie działa, to po prostu dodaje kolejne zdarzenie, które wskazuje na pustą funkcję ...
Fabian Bigler,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.