jQuery - Ereignis auslösen, wenn ein Element aus dem DOM entfernt wird

211

Ich versuche herauszufinden, wie man js-Code ausführt, wenn ein Element von der Seite entfernt wird:

jQuery('#some-element').remove(); // remove some element from the page
/* need to figure out how to independently detect the above happened */

Gibt es eine Veranstaltung, die darauf zugeschnitten ist?

jQuery('#some-element').onremoval( function() {
    // do post-mortem stuff here
});

Vielen Dank.

sa125
quelle
1
Was würde aus Neugier mit dem entfernten Element geschehen wollen?
Natrium
8
Ich habe ein Element, das sich unabhängig an das zu entfernende Teil anfügt. Daher möchte ich erkennen, wann dieses Teil weg ist, um auch dieses Element zu entfernen. Ich könnte das Ganze neu gestalten, aber wenn ich das oben genannte erledige, spare ich viel Zeit (und Code).
Sa125

Antworten:

118

Gerade überprüft, ist es bereits in der aktuellen Version von JQuery integriert:

jQuery - v1.9.1

jQuery UI - v1.10.2

$("#myDiv").on("remove", function () {
    alert("Element was removed");
})

Wichtig : Dies ist die Funktionalität des Jquery-UI- Skripts (nicht von JQuery). Sie müssen also beide Skripte (jquery und jquery-ui) laden, damit es funktioniert. Hier ist ein Beispiel: http://jsfiddle.net/72RTz/

Philipp Munin
quelle
11
Das war sehr hilfreich. Ich habe erfahren, dass diese Funktionalität in der "Widget" -Komponente der jQuery-Benutzeroberfläche enthalten ist, wenn Sie nicht die gesamte Benutzeroberflächenbibliothek herunterladen möchten.
Neil
5
Ist das irgendwo dokumentiert? Ich habe gerade die Dokumentation zum jQuery UI-Widget durchgesehen und konnte keine Erwähnung finden. Ich würde gerne sehen, ob dies offiziell unterstützt wird / irgendwelche Einschränkungen bezüglich der Verwendung ...
Josh
Dieser funktioniert nicht - versucht in jQuery 1.10.2, aber die Antwort unten von @mtkopone funktioniert perfekt, daher würde ich für die Aktualisierung der Antwort in dieser Frage stimmen
Marcin
20
Dieser funktioniert nur teilweise. Wenn Sie dies removefür das Element tun , wird es ausgelöst, aber wenn das Element auf andere Weise zerstört wird, indem es beispielsweise überschrieben wird, funktioniert es nicht.
Carl Smith
Möglicherweise haben Sie Recht, wahrscheinlich gibt es für diesen Fall einen anderen Ereignisnamen. Es wäre großartig, wenn Sie ein jsfiddle-Beispiel für Ihren Fall bereitstellen könnten.
Philipp Munin
195

Hierfür können Sie jQuery-Sonderereignisse verwenden .

In aller Einfachheit,

Konfiguration:

(function($){
  $.event.special.destroyed = {
    remove: function(o) {
      if (o.handler) {
        o.handler()
      }
    }
  }
})(jQuery)

Verwendung:

$('.thing').bind('destroyed', function() {
  // do stuff
})

Nachtrag zur Beantwortung der Kommentare von Pierre und DesignerGuy:

Um den Rückruf beim Aufrufen nicht auszulösen $('.thing').off('destroyed'), ändern Sie die if-Bedingung in:if (o.handler && o.type !== 'destroyed') { ... }

mtkopone
quelle
15
Wirklich schöne Lösung. Ben Alman hat eine schöne Zusammenfassung über besondere Ereignisse für weitere Informationen: benalman.com/news/2010/03/jquery-special-events
antti_s
11
+1 hatte es bisher geschafft, diese besonderen Ereignisse komplett zu verpassen, verdammt nützlich! Eine Sache zu sagen, nachdem Sie gerade das oben Genannte implementiert haben, ist es am besten, zu ändern o.handler(), da o.handler.apply(this,arguments)sonst die Ereignis- und Datenobjekte nicht durch den Ereignis-Listener geleitet werden.
Pebbl
6
Dies funktioniert jedoch nicht, wenn Sie Elemente ohne jQuery entfernen.
Djjeck
5
Dies funktioniert nicht a) wenn Elemente getrennt statt entfernt werden oder b) wenn einige alte Nicht-JQuery-Bibliotheken innerHTML verwenden, um Ihre Elemente zu zerstören (ähnlich wie Djjeck sagte)
Charon ME
5
$('.thing').unbind('destroyed')Beachten Sie, dass der Handler aufgerufen wird, wenn Sie wirklich nervig sein könnten (da Unbind bedeutet, dass der Handler nicht aufgerufen werden soll ...)
Pierre
54

Sie können an das DOMNodeRemoved-Ereignis binden (Teil der DOM Level 3 WC3-Spezifikation).

Funktioniert in IE9, den neuesten Versionen von Firefox und Chrome.

Beispiel:

$(document).bind("DOMNodeRemoved", function(e)
{
    alert("Removed: " + e.target.nodeName);
});

Sie können auch eine Benachrichtigung erhalten, wenn Elemente eingefügt werden, indem Sie an binden DOMNodeInserted

Adam
quelle
13
Hinweis: "Das Hinzufügen von DOM-Mutations-Listenern zu einem Dokument beeinträchtigt die Leistung weiterer DOM-Änderungen an diesem Dokument erheblich (wodurch sie 1,5- bis 7-mal langsamer werden!)." von: developer.mozilla.org/en/DOM/Mutation_events
Matt Crinklaw-Vogt
1
Ich liebe das, obwohl nodeName für mich selten nützlich ist. Ich benutze nur e.target.classNameoder if ($(e.target).hasClass('my-class')) { ....
Micah Alcorn
Dies funktioniert nicht am Element selbst, sondern nur an seinen Kindern.
Xdg
Erstaunliche Antwort! Hat mir wirklich geholfen!
Kir Mazur
Es mag langsam und eine schlechte Idee für Code in der Produktion sein, aber dies scheint die einzige Methode zu sein, die synchron (im Gegensatz zu MutationObserver) und für jeden Knoten (nicht nur über jQuery entfernte Knoten ) funktioniert . Dies ist eine großartige Option zum Debuggen.
Bestimmte Leistung
37

Es gibt kein integriertes Ereignis zum Entfernen von Elementen, aber Sie können eines erstellen, indem Sie die Standard-Entfernungsmethode von jQuery fälschlicherweise erweitern. Beachten Sie, dass der Rückruf aufgerufen werden muss, bevor er tatsächlich entfernt wird, um die Referenz beizubehalten.

(function() {
    var ev = new $.Event('remove'),
        orig = $.fn.remove;
    $.fn.remove = function() {
        $(this).trigger(ev);
        return orig.apply(this, arguments);
    }
})();

$('#some-element').bind('remove', function() {
    console.log('removed!');
    // do pre-mortem stuff here
    // 'this' is still a reference to the element, before removing it
});

// some other js code here [...]

$('#some-element').remove();

Hinweis: Einige Probleme mit dieser Antwort wurden von anderen Postern beschrieben.

  1. Dies funktioniert nicht, wenn der Knoten über html() replace()oder andere jQuery-Methoden entfernt wird
  2. Dieses Ereignis sprudelt
  3. jQuery UI überschreibt auch das Entfernen

Die eleganteste Lösung für dieses Problem scheint zu sein: https://stackoverflow.com/a/10172676/216941

David Hellsing
quelle
2
Dank dafür! Eine kleine Ergänzung: Da das Ereignis zum Entfernen sprudelt, erhalten Sie es auch, wenn ein Kind entfernt wird. Schreiben Sie den Handler also besser so:$('#some-element').bind('remove', function(ev) { if (ev.target === this) { console.log('removed!'); } });
meyertee
3
Das hat bei mir nicht sofort geklappt - ich musste das Ergebnis von orig.apply zurückgeben.
Furturtle
1
Eigentlich ist es @Adam, aber es ist browserübergreifend kompatibel. Mit den Ergänzungen von meyertee / fturtle ist dies eine absolut zuverlässige Lösung, solange Sie nur Elemente mit dieser Methode entfernen, anstatt HTML usw. zu ändern / zu leeren Sie sollten in einer App auf Geschäftsereignisse achten, nicht auf DOM-Ereignisse, deren Struktur sich mit der weiteren Entwicklung wahrscheinlich ändern wird. Das Abonnieren von DOM-Mutationsereignissen bedeutet außerdem, dass Ihr Programm möglicherweise in komplexen DOM-Hierarchien zurückbleibt.
Will Morgan
1
Meine einzige Uneinigkeit über die Genauigkeit ist die Aussage: "Es gibt kein integriertes Ereignis zum Entfernen von Elementen". Es gibt ein integriertes Ereignis für Browser, die DOM-Ereignisse der Stufe 3 implementieren (wie in meiner Antwort beschrieben).
Adam
4
Während dies Elemente erkennt, die mit der Funktion 'Entfernen' entfernt wurden, werden Elemente, die mit anderen Mitteln entfernt wurden (z. B. mithilfe von jQuerys HTML, Ersetzen usw.), nicht erkannt. Weitere Informationen finden Sie in meiner Antwort.
Zah
32

Anspannen .remove()ist nicht der beste Weg , dies zu handhaben, da es viele Möglichkeiten gibt , Elemente von der Seite zu entfernen (zB durch Verwendung .html(), .replace()usw.).

Um verschiedene Gefahren für Speicherverluste zu vermeiden, versucht jQuery intern, die Funktion jQuery.cleanData()für jedes entfernte Element aufzurufen, unabhängig von der Methode, mit der es entfernt wurde.

Weitere Informationen finden Sie in dieser Antwort: Javascript-Speicherlecks

Um die besten Ergebnisse zu erzielen , sollten Sie die cleanDataFunktion einbinden. Genau das macht das Plugin jquery.event.destroyed :

http://v3.javascriptmvc.com/jquery/dist/jquery.event.destroyed.js

zah
quelle
Dieser Einblick cleanDatawar sehr hilfreich für mich! Vielen Dank, Joe :)
Petr Vostrel
1
Gute Antwort, aber das funktioniert immer noch nur für jQuery-Methoden. Wenn Sie sich in eine andere Plattform integrieren, die Ihnen den Teppich "herausziehen" kann, ist Adams Antwort am sinnvollsten.
Bron Davies
7

Für diejenigen, die die jQuery-Benutzeroberfläche verwenden:

jQuery UI hat einige der jQuery - Methoden außer Kraft gesetzt ein implementieren removeEreignis , das nicht nur behandelt wird , wenn Sie explizit das gegebene Element entfernen, sondern auch , wenn das Element wird aus dem DOM durch irgendwelche selbstreinig jQuery Methoden entfernt (zB replace, htmlusw.) . Auf diese Weise können Sie im Grunde genommen dieselben Ereignisse einbinden, die ausgelöst werden, wenn jQuery die mit einem DOM-Element verknüpften Ereignisse und Daten "bereinigt".

John Resig hat angedeutet, dass er offen für die Idee ist, dieses Ereignis in einer zukünftigen Version von jQuery Core zu implementieren, aber ich bin mir nicht sicher, wo es derzeit steht.

StriplingWarrior
quelle
6

Es ist nur jQuery erforderlich (keine jQuery-Benutzeroberfläche erforderlich)

( Ich habe diese Erweiterung aus dem jQuery-UI-Framework extrahiert. )

Funktioniert mit: empty()und html()undremove()

$.cleanData = ( function( orig ) {
    return function( elems ) {
        var events, elem, i;
        for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) {
            try {

                // Only trigger remove when necessary to save time
                events = $._data( elem, "events" );
                if ( events && events.remove ) {
                    $( elem ).triggerHandler( "remove" );
                }

            // Http://bugs.jquery.com/ticket/8235
            } catch ( e ) {}
        }
        orig( elems );
    };
} )( $.cleanData );

Mit dieser Lösung können Sie auch den Event-Handler aufheben .

$("YourElemSelector").off("remove");

Versuch es! - Beispiel

$.cleanData = (function(orig) {
  return function(elems) {
    var events, elem, i;
    for (i = 0;
      (elem = elems[i]) != null; i++) {
      try {

        // Only trigger remove when necessary to save time
        events = $._data(elem, "events");
        if (events && events.remove) {
          $(elem).triggerHandler("remove");
        }

        // Http://bugs.jquery.com/ticket/8235
      } catch (e) {}
    }
    orig(elems);
  };
})($.cleanData);


$("#DivToBeRemoved").on("remove", function() {
  console.log("div was removed event fired");
});

$("p").on("remove", function() {
  console.log("p was removed event fired");
});

$("span").on("remove", function() {
  console.log("span was removed event fired");
});

// $("span").off("remove");

$("#DivToBeRemoved").on("click", function() {
  console.log("Div was clicked");
});

function RemoveDiv() {
  //       $("#DivToBeRemoved").parent().html("");    
  $("#DivToBeRemoved").remove();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h3>OnRemove event handler attached to elements `div`, `p` and `span`.</h3>
<div class="container">
  <br>
  <button onclick="RemoveDiv();">Click here to remove div below</button>
  <div id="DivToBeRemoved">
    DIV TO BE REMOVED 
    contains 1 p element 
    which in turn contains a span element
    <p>i am p (within div)
      <br><br><span>i am span (within div)</span></p>
  </div>
</div>

Zusätzliche Demo - jsBin

Legenden
quelle
4

Ich konnte diese Antwort nicht zum Aufheben der Bindung verwenden (trotz des hier gezeigten Updates ), konnte aber einen Weg finden, um sie zu umgehen. Die Antwort war, ein spezielles Ereignis "destroy_proxy" zu erstellen, das ein "zerstörtes" Ereignis auslöste. Sie setzen den Ereignis-Listener sowohl auf 'destroy_proxy' als auch auf 'destroy'. Wenn Sie dann die Bindung aufheben möchten, lösen Sie einfach die Verbindung zwischen dem "zerstörten" Ereignis:

var count = 1;
(function ($) {
    $.event.special.destroyed_proxy = {
        remove: function (o) {
            $(this).trigger('destroyed');
        }
    }
})(jQuery)

$('.remove').on('click', function () {
    $(this).parent().remove();
});

$('li').on('destroyed_proxy destroyed', function () {
    console.log('Element removed');
    if (count > 2) {
        $('li').off('destroyed');
        console.log('unbinded');
    }
    count++;
});

Hier ist eine Geige

Bill Johnston
quelle
Was ist mit der Browserkompatibilität hier? Funktioniert es noch
Vladislav Rastrusny
3

Ich mag die Antwort von mtkopone mit jQuery-Sonderereignissen, aber beachte, dass es nicht funktioniert, a) wenn Elemente getrennt statt entfernt werden oder b) wenn einige alte Nicht-jquery-Bibliotheken innerHTML verwenden, um Ihre Elemente zu zerstören

Charon ME
quelle
3
Ich habe abgestimmt, weil die Frage explizit nach .remove () gestellt und nicht getrennt wurde. detachwird insbesondere verwendet, um keine Bereinigung auszulösen, da das Element wahrscheinlich später wieder angebracht werden soll. b) ist immer noch wahr und hat noch keine zuverlässige Handhabung, zumindest da DOM-Mutationsereignisse weit verbreitet sind.
Pierre
4
Ich wette, Sie haben dies getan, um das "Kritiker" -Abzeichen zu erhalten;)
Charon ME
1

Ich bin mir nicht sicher, ob es dafür ein Ereignishandle gibt. Sie müssten also eine Kopie des DOM aufbewahren und in einer Art Abfrageschleife mit dem vorhandenen DOM vergleichen - was ziemlich unangenehm sein könnte. Firebug tut dies jedoch - wenn Sie den HTML-Code überprüfen und einige DOM-Änderungen ausführen, werden die Änderungen in der Firebug-Konsole für kurze Zeit gelb hervorgehoben.

Alternativ können Sie eine Entfernungsfunktion erstellen ...

var removeElements = function(selector) {
    var elems = jQuery(selector);

    // Your code to notify the removal of the element here...
    alert(elems.length + " elements removed");

    jQuery(selector).remove();
};

// Sample usage
removeElements("#some-element");
removeElements("p");
removeElements(".myclass");
Fenton
quelle
1
+1 für diese Idee. Sie können jedoch auch jQuery (Plugin-Stil) erweitern, um einen Standard-jQuery-Aufruf zu erhalten, z. B.: $ ('. ItemToRemove'). CustomRemove ();. Sie können es auch so gestalten, dass ein Rückruf als Parameter akzeptiert wird.
user113716
1

So erstellen Sie einen jQuery Live Listener zum Entfernen :

$(document).on('DOMNodeRemoved', function(e)
{
  var $element = $(e.target).find('.element');
  if ($element.length)
  {
    // do anything with $element
  }
});

Oder:

$(document).on('DOMNodeRemoved', function(e)
{
  $(e.target).find('.element').each(function()
  {
    // do anything with $(this)
  }
});
Buzogany Laszlo
quelle
1
Es wäre großartig, dazu frei zu sein. Leider ist es nicht zuverlässig, da die Mutationsereignisse veraltet sind: developer.mozilla.org/en-US/docs/Web/Guide/Events/…
Kamafeather
1

Das Ereignis "remove" aus jQuery funktioniert ohne Zusatz einwandfrei. Es könnte mit der Zeit zuverlässiger sein, einen einfachen Trick zu verwenden, anstatt jQuery zu patchen.

Ändern oder fügen Sie einfach ein Attribut in dem Element hinzu, das Sie aus dem DOM entfernen möchten. Somit können Sie mit dem Attribut "do_not_count_it" jede Aktualisierungsfunktion auslösen, die Elemente auf dem Weg zur Zerstörung einfach ignoriert.

Angenommen, wir haben eine Tabelle mit Zellen, die den Preisen entsprechen, und Sie müssen nur den letzten Preis anzeigen: Dies ist der Selektor, der ausgelöst wird, wenn eine Preiszelle gelöscht wird (in jeder Zeile der Tabelle befindet sich eine Schaltfläche, die dies nicht anzeigt) Hier)

$('td[validity="count_it"]').on("remove", function () {
    $(this).attr("validity","do_not_count_it");
    update_prices();
});

Und hier ist eine Funktion, die den letzten Preis in der Tabelle findet, ohne den letzten zu berücksichtigen, wenn dieser entfernt wurde. In der Tat wird das Element noch nicht entfernt, wenn das Ereignis "Entfernen" ausgelöst wird und wenn diese Funktion aufgerufen wird.

function update_prices(){
      var mytable=$("#pricestable");
      var lastpricecell = mytable.find('td[validity="count_it"]').last();
}

Am Ende funktioniert die Funktion update_prices () einwandfrei, und danach wird das DOM-Element entfernt.

philippe
quelle
0

Verweis auf @ David Antwort:

Wenn Sie dies mit einer anderen Funktion tun möchten, z. html () wie in meinem Fall, vergessen Sie nicht, return in der neuen Funktion hinzuzufügen:

(function() {
    var ev = new $.Event('html'),
        orig = $.fn.html;
    $.fn.html = function() {
        $(this).trigger(ev);
        return orig.apply(this, arguments);
    }
})();
Patryk M.
quelle
0

Dies.

$.each(
  $('#some-element'), 
        function(i, item){
            item.addEventListener('DOMNodeRemovedFromDocument',
                function(e){ console.log('I has been removed'); console.log(e);
                })
         })
Matas Vaitkevicius
quelle
1
DOMNodeRemovedFromDocument scheint in Firefox nicht unterstützt zu werden. Vielleicht versuchen Sie stattdessen MutationObserver ?
EricP
0

Eine Erweiterung von Adams Antwort, falls Sie die Standardeinstellung überwinden müssen. Hier ist eine Problemumgehung:

$(document).on('DOMNodeRemoved', function(e){
        if($(e.target).hasClass('my-elm') && !e.target.hasAttribute('is-clone')){
            let clone = $(e.target).clone();
            $(clone).attr('is-clone', ''); //allows the clone to be removed without triggering the function again

            //you can do stuff to clone here (ex: add a fade animation)

            $(clone).insertAfter(e.target);
            setTimeout(() => {
                //optional remove clone after 1 second
                $(clone).remove();
            }, 1000);
        }
    });
SwiftNinjaPro
quelle
0

Wir können auch DOMNodeRemoved verwenden:

$("#youridwhichremoved").on("DOMNodeRemoved", function () {
// do stuff
})
Den Pat
quelle