Ereignisbindung für dynamisch erstellte Elemente?

1677

Ich habe ein bisschen Code, in dem ich alle Auswahlfelder auf einer Seite durchlaufe und ein .hoverEreignis an sie binde, um ein bisschen mit ihrer Breite herumzuspielen mouse on/off.

Dies geschieht auf Seite bereit und funktioniert einwandfrei.

Das Problem, das ich habe, ist, dass bei Auswahlfeldern, die ich nach der ersten Schleife über Ajax oder DOM hinzufüge, das Ereignis nicht gebunden ist.

Ich habe dieses Plugin gefunden ( jQuery Live Query Plugin ), aber bevor ich meinen Seiten mit einem Plugin weitere 5 KB hinzufüge, möchte ich sehen, ob jemand einen Weg kennt, dies zu tun, entweder direkt mit jQuery oder mit einer anderen Option.

Eli
quelle

Antworten:

2250

Ab jQuery 1.7 sollten Sie Folgendes verwenden jQuery.fn.on:

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

Zuvor war der empfohlene Ansatz live():

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

Allerdings live()wurde in 1.7 für veraltet on()und vollständig in 1.9 entfernt. Die live()Unterschrift:

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

... kann durch folgende on()Unterschrift ersetzt werden:

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

Wenn Ihre Seite beispielsweise dynamisch Elemente mit dem Klassennamen erstellt dosomething, binden Sie das Ereignis an ein übergeordnetes Element, das bereits vorhanden ist (dies ist der Kern des Problems hier. Sie benötigen etwas, an das gebunden werden kann, und binden Sie nicht an das dynamischer Inhalt), dies kann sein (und die einfachste Option) ist document. Bedenken Sie jedoch, dass dies documentmöglicherweise nicht die effizienteste Option ist .

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

Alle Eltern, die zum Zeitpunkt der Ereignisbindung vorhanden sind, sind in Ordnung. Zum Beispiel

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

würde gelten für

<div class="buttons">
    <!-- <button>s that are generated dynamically and added here -->
</div>
Popnoodles
quelle
7
Beachten Sie, dass die Live-Methode nur für bestimmte Ereignisse funktioniert und nicht für andere wie geladene Metadaten. (Siehe den Abschnitt mit den Einschränkungen in der Dokumentation.)
Sam Dutton
48
Weitere Informationen zur Delegation von Veranstaltungen finden Sie hier: learn.jquery.com/events/event-delegation .
Felix Kling
9
Gibt es eine Möglichkeit, dies mit reinem Javascript / Vanilla Js zu erreichen?
Ram Patra
15
@Ramswaroop Alles, was Sie in jQuery tun können, kann ohne jQuery ausgeführt werden. Hier ist ein gutes Beispiel für eine Ereignisdelegation ohne jQuery
Dave
5
@ Dave Ich frage mich, warum die Antwort, auf die Sie hingewiesen haben, hier nicht aufgeführt ist. Eli hat eindeutig nach einer Lösung ohne Plugin gefragt, wenn möglich.
Ram Patra
377

Es gibt eine gute Erklärung in der Dokumentation von jQuery.fn.on.

Zusamenfassend:

Ereignishandler sind nur an die aktuell ausgewählten Elemente gebunden. Sie müssen zum Zeitpunkt des Aufrufs Ihres Codes auf der Seite vorhanden sein .on().

Daher muss im folgenden Beispiel #dataTable tbody trvorhanden sein, bevor der Code generiert wird.

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

Wenn neues HTML in die Seite eingefügt wird, ist es vorzuziehen, delegierte Ereignisse zu verwenden, um einen Ereignishandler anzuhängen, wie im Folgenden beschrieben.

Delegierte Ereignisse haben den Vorteil, dass sie Ereignisse von untergeordneten Elementen verarbeiten können, die zu einem späteren Zeitpunkt zum Dokument hinzugefügt werden. Wenn die Tabelle beispielsweise vorhanden ist, die Zeilen jedoch dynamisch mithilfe von Code hinzugefügt werden, wird dies wie folgt behandelt:

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

Zusätzlich zu ihrer Fähigkeit, Ereignisse für noch nicht erstellte untergeordnete Elemente zu verarbeiten, besteht ein weiterer Vorteil delegierter Ereignisse darin, dass sie möglicherweise einen viel geringeren Overhead verursachen, wenn viele Elemente überwacht werden müssen. In einer Datentabelle mit 1.000 Zeilen wird im tbodyersten Codebeispiel ein Handler an 1.000 Elemente angehängt.

Ein Ansatz für delegierte Ereignisse (das zweite Codebeispiel) hängt einen Ereignishandler nur an ein Element an, das tbody, und das Ereignis muss nur eine Ebene hochblasen (von angeklickt trbis tbody).

Hinweis: Delegierte Ereignisse funktionieren nicht für SVG .

Ronen Rabinovici
quelle
11
Dies wäre eine besser akzeptierte Antwort, da es schneller wäre, aus der spezifischen Tabelle zu delegieren, als aus dem gesamten Dokument (der Suchbereich wäre viel kleiner)
msanjay
5
@msanjay: Obwohl es bevorzugt wird, die Suche näher an die Elemente zu richten, ist der Unterschied zwischen Suche und Geschwindigkeit in der Praxis sehr gering. Sie müssten 50.000 Mal pro Sekunde klicken, um etwas zu bemerken :)
Gone Coding
2
Kann dieser Ansatz auf die Behandlung von Kontrollkästchen-Klicks in einer Zeile angewendet werden, indem trzu einem geeigneten Selektor gewechselt wird , z. B. 'input[type=checkbox]'würde dies automatisch für neu eingefügte Zeilen behandelt?
JoeBrockhaus
1
Vielen Dank! Das löst mein Problem. Diese einfache Aussage war mein Problem: "Event-Handler sind nur an die aktuell ausgewählten Elemente gebunden. Sie müssen zum Zeitpunkt des Aufrufs Ihres Codes auf ... auf der Seite vorhanden sein."
Dennis Fazekas
216

Dies ist eine reine JavaScript- Lösung ohne Bibliotheken oder Plugins:

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

wo hasClassist

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

Live demo

Der Kredit geht an Dave und Sime Vidas

Mit modernerem JS hasClasskann Folgendes implementiert werden:

function hasClass(elem, className) {
    return elem.classList.contains(className);
}
Ram Patra
quelle
6
Sie können Element.classList verwenden, anstatt zu teilen
Eugen Konkov
2
@EugenKonkov Element.classListwird von älteren Browsern nicht unterstützt. Zum Beispiel IE <9.
Ram Patra
2
Ein schöner Artikel darüber, wie man Dinge mit Vanille-Skript anstelle von jQuery erledigt
Ram Patra
2
Wie wäre es mit Sprudeln? Was ist, wenn das Klickereignis bei einem untergeordneten Element des Elements aufgetreten ist, an dem Sie interessiert sind?
Andreas Trantidis
73

Sie können Objekten Ereignisse hinzufügen, wenn Sie sie erstellen. Wenn Sie mehreren Objekten zu unterschiedlichen Zeiten dieselben Ereignisse hinzufügen, ist das Erstellen einer benannten Funktion möglicherweise der richtige Weg.

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

Sie können Ihren Ereignisbindungsaufruf einfach in eine Funktion einbinden und dann zweimal aufrufen: einmal im Dokument bereit und einmal nach Ihrem Ereignis, bei dem die neuen DOM-Elemente hinzugefügt werden. Wenn Sie dies tun, sollten Sie vermeiden, dasselbe Ereignis zweimal an die vorhandenen Elemente zu binden, sodass Sie entweder die vorhandenen Ereignisse aufheben oder (besser) nur an die neu erstellten DOM-Elemente binden müssen. Der Code würde ungefähr so ​​aussehen:

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

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

// ... add elements ...
addCallbacks($(".myNewElements"))
Greg Borenstein
quelle
2
Dieser Beitrag hat mir wirklich geholfen, ein Problem zu verstehen, bei dem ich das gleiche Formular geladen und 1,2,4,8,16 ... Einsendungen erhalten habe. Anstatt .live () zu verwenden, habe ich in meinem .load () - Rückruf nur .bind () verwendet. Problem gelöst. Vielen Dank!
Thomas McCabe
42

Versuchen Sie, .live()anstelle von zu verwenden .bind(); Das .live()wird .hoveran Ihr Kontrollkästchen gebunden, nachdem die Ajax-Anforderung ausgeführt wurde.

user670265
quelle
32
Die Methode live()wurde in Version 1.7 zugunsten von onVersion 1.9 veraltet und in Version 1.9 gelöscht.
Chridam
27

Ereignisbindung für dynamisch erstellte Elemente

Einzelelement:

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

Untergeordnetes Element:

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

Beachten Sie die hinzugefügten *. Für alle untergeordneten Elemente dieses Elements wird ein Ereignis ausgelöst.

Ich habe bemerkt, dass:

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

Es funktioniert nicht mehr, aber es hat vorher funktioniert. Ich habe jQuery von Google CDN verwendet , weiß aber nicht, ob sie es geändert haben.

MadeInDreams
quelle
Ja, und sie sagen nicht (document.body), dass es Vorfahren gibt, die so ziemlich alles sein könnten
MadeInDreams
25

Mit der live () -Methode können Sie Elemente (auch neu erstellte) an Ereignisse und Handler binden, z. B. das Ereignis onclick.

Hier ist ein Beispielcode, den ich geschrieben habe, in dem Sie sehen können, wie die live () -Methode ausgewählte Elemente, auch neu erstellte, an Ereignisse bindet:

<!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>
Fazi
quelle
23

Ich bevorzuge die Verwendung des Selektors und wende ihn auf das Dokument an.

Dies bindet sich an das Dokument und gilt für die Elemente, die nach dem Laden der Seite gerendert werden.

Zum Beispiel:

$(document).on("click", 'selector', function() {
    // Your code here
});
Vatsal
quelle
2
Der Selektor sollte nicht von $ eingeschlossen werden, daher lautet das korrekte Format $ (document) .on ("click", "selector", function () {// Ihr Code hier});
Autopilot
1
Es ist auch sinnlos, ein jQuery-Objekt um die selectorVariable zu wickeln , wenn es entweder eine Zeichenfolge oder ein Element-Objekt enthalten muss, das Sie direkt an das Argument vonon()
Rory McCrossan
20

Eine andere Lösung besteht darin, den Listener beim Erstellen des Elements hinzuzufügen. Anstatt den Hörer in den Körper zu setzen, setzen Sie den Hörer in dem Moment in das Element, in dem Sie ihn erstellen:

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

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


function goToGoogle(event){
    window.location.replace("http://www.google.com");
}
Martin Da Rosa
quelle
Ihr Code enthält 1 Fehler: myElement.append('body');muss sein myElement.appendTo('body');. Auf der anderen Seite ist myElementes einfacher und kürzer , wenn die weitere Verwendung der Variablen nicht erforderlich ist :$('body').append($('<button/>', { text: 'Go to Google!' }).bind( 'click', goToGoogle));
ddlab
17

Versuchen Sie es so -

$(document).on( 'click', '.click-activity', function () { ... });
Rohit Suthar
quelle
16

Beachten Sie die Klasse "MAIN", in der das Element platziert ist, z.

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

Im obigen Szenario ist das MAIN-Objekt, das von jQuery überwacht wird, "Container".

Dann werden Sie im Grunde Elemente Namen unter Container haben wie ul, li, und select:

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

Dies erfolgt durch Ereignisdelegation . Das Ereignis wird an das Wrapper-Class-Element gebunden, jedoch an das Selector-Class-Element delegiert. So funktioniert es.

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

Hinweis:

Wrapper-Class-Element kann alles sein, z. Dokument, Körper oder Ihre Hülle. Wrapper sollte bereits vorhanden sein . Muss selectorjedoch nicht unbedingt beim Laden der Seite angezeigt werden. Es kann später kommen und das Ereignis wird selector ohne Fehler weitergebunden .

Mustkeem K.
quelle
14

Du könntest benutzen

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

oder

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

Diese beiden Methoden sind äquivalent, haben jedoch eine unterschiedliche Reihenfolge der Parameter.

Siehe: jQuery-Delegatenereignis

Mensur Grišević
quelle
2
delegate()ist jetzt veraltet. Benutze es nicht.
Rory McCrossan
14

Sie können ein Ereignis an ein Element anhängen, wenn es dynamisch mit erstellt wird jQuery(html, attributes).

Ab jQuery 1.8 kann jede jQuery-Instanzmethode (eine Methode von jQuery.fn) als Eigenschaft des an den zweiten Parameter übergebenen Objekts verwendet werden:

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>

guest271314
quelle
11

Aus diesem Grund reagieren dynamisch erstellte Elemente nicht auf Klicks:

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>

Um dieses Problem zu umgehen, müssen Sie alle Klicks abhören und das Quellelement überprüfen:

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>

Dies wird als "Ereignisdelegation" bezeichnet. Gute Nachrichten, es ist eine integrierte Funktion in 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>

Blatt
quelle
10

Jeder p Arent , das existiert das Ereignis zu dem Zeitpunkt gebunden ist , und wenn Ihre Seite wurde dynamisch Elemente zu schaffen mit der Klassennamen Taste würden Sie das Ereignis zu einem Elternteil binden , die bereits vorhanden ist

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

Ankit Kathiriya
quelle
7

Binden Sie das Ereignis an ein übergeordnetes Element, das bereits vorhanden ist:

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

Verwenden Sie die .on()Methode von jQuery http://api.jquery.com/on/ , um Ereignishandler an das Live-Element anzuhängen.

Auch ab Version 1.9 wird die .live()Methode entfernt.

Kalpesh Patel
quelle
3

Ich bevorzuge es, Ereignis-Listener modular bereitzustellen, anstatt einen documentLevel-Ereignis-Listener zu erstellen. Also, ich mag unten. Beachten Sie, dass Sie ein Element nicht mit demselben Ereignis-Listener überzeichnen können. Machen Sie sich also keine Sorgen, dass Sie einen Listener mehr als einmal anhängen müssen - nur einer bleibt hängen.

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
}

Ronnie Royston
quelle
Ich bevorzuge diese Implementierung; Ich muss nur einen Rückruf einrichten
William
3

Eine weitere flexible Lösung zum Erstellen von Elementen und zum Binden von Ereignissen ( Quelle )

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

Hinweis: Dadurch wird für jedes Element eine Ereignishandlerinstanz erstellt (kann die Leistung beeinträchtigen, wenn sie in Schleifen verwendet wird).

Prasad De Silva
quelle
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>
Fakhrul Hasan
quelle
3
Dieses Code-Snippet kann das Problem zwar lösen, erklärt jedoch nicht, warum oder wie es die Frage beantwortet. Bitte geben Sie eine Erklärung für Ihren Code an, da dies wirklich zur Verbesserung der Qualität Ihres Beitrags beiträgt. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen.
Palec
0

Ich suchte nach einer Lösung, um problemlos dynamisch hinzugefügte Elemente zu erhalten $.bindund zu $.unbindarbeiten.

As on () macht den Trick, Ereignisse anzuhängen, um eine falsche Bindung zu denen zu erzeugen, zu denen ich gekommen bin:

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

// unbind the click
$('body').on('click', 'button.send', function(){} );
Evhz
quelle
Das Aufheben der Bindung funktioniert nicht, dies fügt einfach ein weiteres Ereignis hinzu, das auf eine leere Funktion hinweist ...
Fabian Bigler