jQuery - Evento trigger quando un elemento viene rimosso dal DOM


211

Sto cercando di capire come eseguire del codice js quando un elemento viene rimosso dalla pagina:

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

c'è un evento su misura per questo, qualcosa del tipo:

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

Grazie.


1
per curiosità, cosa vorrebbe fare dell'elemento che è stato rimosso?
Natrium,

8
Ho un elemento che si attacca in modo indipendente al pezzo che rimuovo, quindi voglio rilevare quando quel pezzo è andato per eliminare anche quell'elemento. Potrei riprogettare il tutto, ma realizzare quanto sopra mi farà risparmiare un sacco di tempo (e codice).
sa125,

Risposte:


118

Appena verificato, è già integrato nella versione corrente di JQuery:

jQuery - v1.9.1

Interfaccia utente di jQuery - v1.10.2

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

Importante : questa è la funzionalità dello script dell'interfaccia utente di Jquery (non JQuery), quindi è necessario caricare entrambi gli script (jquery e jquery-ui) per farlo funzionare. Ecco un esempio: http://jsfiddle.net/72RTz/


11
Questo è stato molto utile. Ho imparato che questa funzionalità è all'interno del componente "Widget" dell'interfaccia utente di jQuery se non stai cercando di scaricare l'intera libreria dell'interfaccia utente.
Neil,

5
Questo è documentato ovunque? Ho appena consultato la documentazione del widget dell'interfaccia utente jQuery e non sono riuscito a trovare una menzione di questo. Mi piacerebbe vedere se questo è ufficialmente supportato / eventuali avvertenze su come usarlo ...
josh

Questo non funziona - provato in jQuery 1.10.2, tuttavia la risposta sotto di @mtkopone funziona perfettamente, quindi voterei per l'aggiornamento della risposta in quella domanda
Marcin

20
Questo funziona solo in parte. Se lo fai removesull'elemento che spara, ma se l'elemento viene distrutto in qualche altro modo, essendo sovrascritto diciamo, non funziona.
Carl Smith,

Potresti avere ragione, probabilmente c'è un altro nome di evento per questo caso. Sarebbe bello se puoi fornire un campione jsfiddle per il tuo caso.
Philipp Munin,

195

È possibile utilizzare eventi speciali jQuery per questo.

In tutta semplicità,

Impostare:

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

Uso:

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

Addendum per rispondere ai commenti di Pierre e DesignerGuy:

Per non attivare il callback durante la chiamata $('.thing').off('destroyed'), modificare la condizione if in:if (o.handler && o.type !== 'destroyed') { ... }


15
Soluzione davvero bella. Ben Alman ha una bella descrizione di eventi speciali per maggiori informazioni: benalman.com/news/2010/03/jquery-special-events
antti_s

11
+1 era riuscito a perdere completamente questi eventi speciali fino ad ora, dannatamente utile! Una cosa da Stato avendo appena realizzato quanto sopra, sarebbe meglio cambiare o.handler()per o.handler.apply(this,arguments)altro gli oggetti evento e dati non vengono passati attraverso il listener di eventi.
Pebbl

6
Tuttavia, ciò non funziona quando si rimuovono elementi senza jQuery.
Djjeck,

5
questo non funziona a) quando gli elementi vengono staccati anziché rimossi eb) quando alcune vecchie librerie non jquery usano innerHTML per distruggere i tuoi elementi (simile a quello che diceva djjeck)
Charon ME

5
Fai attenzione a che il gestore venga chiamato quando potresti $('.thing').unbind('destroyed')essere davvero noioso (dato che unbind significa che non vogliamo che venga chiamato il gestore ...)
Pierre

54

È possibile associare l'evento DOMNodeRemoved (parte delle specifiche WC3 DOM Level 3).

Funziona in IE9, ultime versioni di Firefox e Chrome.

Esempio:

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

Puoi anche ricevere una notifica quando gli elementi vengono inseriti legando a DOMNodeInserted


13
Nota: "L'aggiunta di listener di mutazioni DOM a un documento degrada profondamente le prestazioni di ulteriori modifiche DOM a quel documento (rendendole 1,5 - 7 volte più lente!)." da: developer.mozilla.org/it/DOM/Mutation_events
Matt Crinklaw-Vogt

1
Adoro questo, sebbene nodeName sia raramente utile per me. Io uso solo e.target.classNameo if ($(e.target).hasClass('my-class')) { ....
Micah Alcorn,

Questo non funziona sull'elemento stesso, ma solo sui suoi figli.
Xdg,

Risposta incredibile! Mi ha davvero aiutato!
Kir Mazur,

Potrebbe essere lento e una cattiva idea per il codice in produzione, ma questo sembra l'unico metodo che funziona in modo sincrono (a differenza di MutationObserver) e per qualsiasi nodo (non solo nodi rimossi tramite jQuery). Questa è un'ottima opzione per il debug.
Certain Performance

38

Non esiste un evento incorporato per la rimozione di elementi, ma è possibile crearne uno estendendo il metodo di rimozione predefinito di jQuery. Si noti che il callback deve essere chiamato prima di rimuoverlo effettivamente per mantenere il riferimento.

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

Nota: alcuni problemi con questa risposta sono stati delineati da altri poster.

  1. Questo non funzionerà quando il nodo viene rimosso tramite html() replace()o altri metodi jQuery
  2. Questo evento si risolve
  3. Anche le sostituzioni dell'interfaccia utente jQuery vengono rimosse

La soluzione più elegante a questo problema sembra essere: https://stackoverflow.com/a/10172676/216941


2
Grazie per quello! Una piccola aggiunta: poiché l'evento remove rimuove, lo riceveresti anche quando un bambino viene rimosso, quindi è meglio scrivere il gestore in questo modo:$('#some-element').bind('remove', function(ev) { if (ev.target === this) { console.log('removed!'); } });
meyertee

3
Questo non ha funzionato per me - ho dovuto restituire il risultato da orig.apply.
fturtle,

1
In realtà, @Adam, lo è, ma è compatibile con più browser. Con le aggiunte di meyertee / fturtle è una soluzione perfettamente affidabile, purché rimuova solo elementi con questo metodo, piuttosto che modificare / svuotare HTML ecc. Per qualcosa di più flessibile, sì, gli eventi di mutazione DOM vanno bene, ma sono stato scettico su di loro come dovresti ascoltare eventi aziendali in un'app, non quelli DOM la cui struttura potrebbe cambiare con l'ulteriore sviluppo. Inoltre, abbonarsi agli eventi di mutazione DOM significa che il programma è potenzialmente suscettibile al rallentamento in gerarchie DOM complesse.
Will Morgan,

1
Il mio unico giudizio sull'accuratezza è l'affermazione: "non esiste un evento incorporato per la rimozione di elementi"; esiste un evento integrato per i browser che implementano eventi DOM di livello 3 (come dettagliato nella mia risposta).
Adam,

4
Mentre questo rileverà gli elementi rimossi usando la funzione 'remove', non riuscirà a rilevare gli elementi rimossi con altri mezzi (es. Usando html di jQuery, sostituisci, ecc.). Per favore, vedi la mia risposta per una soluzione più completa.
zah,

32

Aggancio .remove()non è il modo migliore per gestire questo come ci sono molti modi per rimuovere gli elementi dalla pagina (ad esempio utilizzando .html(), .replace()ecc).

Al fine di prevenire vari rischi di perdita di memoria, internamente jQuery tenterà di chiamare la funzione jQuery.cleanData() per ciascun elemento rimosso indipendentemente dal metodo utilizzato per rimuoverlo.

Vedi questa risposta per maggiori dettagli: perdite di memoria javascript

Quindi, per i migliori risultati, dovresti agganciare la cleanDatafunzione, che è esattamente ciò che jquery.event.destroised plugin :

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


Questa intuizione mi è cleanDatastata molto utile! Grazie mille, Joe :)
Petr Vostrel,

1
Bella risposta, ma funziona ancora solo con i metodi jQuery. Se ti stai integrando con un'altra piattaforma che può "tirare fuori il tappeto" da sotto di te, la risposta di Adam ha più senso.
Bron Davies,

7

Per coloro che usano l'interfaccia utente di jQuery:

jQuery UI ha ignorato alcuni dei metodi jQuery per realizzare un removeevento che viene gestito non solo quando si rimuove in modo esplicito l'elemento dato, ma anche se l'elemento viene rimosso dal DOM da qualsiasi metodo di jQuery autopulenti (ad esempio replace, htmle così via) . Questo in sostanza ti consente di mettere un hook negli stessi eventi che vengono generati quando jQuery "ripulisce" gli eventi e i dati associati a un elemento DOM.

John Resig ha dichiarato di essere aperto all'idea di implementare questo evento in una versione futura di jQuery core, ma non sono sicuro di dove si trovi attualmente.


6

È richiesto solo jQuery (non è necessaria l'interfaccia utente di jQuery)

( Ho estratto questa estensione dal framework dell'interfaccia utente jQuery )

Funziona con: empty()e html()eremove()

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

Con questa soluzione è anche possibile separare il gestore eventi.

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

Provalo! - Esempio

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

Demo aggiuntiva - jsBin


4

Non sono riuscito a far sì che questa risposta funzionasse con il non vincolo (nonostante l'aggiornamento vedere qui ), ma sono riuscito a trovare un modo per aggirare il problema. La risposta è stata quella di creare un evento speciale "destroy_proxy" che ha attivato un evento "distrutto". Metti il ​​listener di eventi sia su 'distrutto_proxy' che su 'distrutto', quindi quando vuoi separarti, devi semplicemente sciogliere l'evento 'distrutto':

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

Ecco un violino


Che dire della compatibilità del browser qui? Funziona ancora?
Vladislav Rastrusny,

3

Mi piace la risposta di mtkopone usando gli eventi speciali di jQuery, ma nota che non funziona a) quando gli elementi sono staccati anziché rimossi o b) quando alcune vecchie librerie non jquery usano innerHTML per distruggere i tuoi elementi


3
Ho effettuato il downvoting perché la domanda posta esplicitamente per l'uso .remove () non si stacca. detachviene utilizzato soprattutto per non attivare la pulizia poiché è probabile che l'elemento venga ricollegato in un secondo momento. b) è ancora vero e non ha ancora una gestione affidabile, almeno dal momento che gli eventi di mutazione DOM sono ampiamente implementati.
Pierre,

4
Scommetto che l'hai fatto solo per ottenere il badge "critico";)
Charon ME,

1

Non sono sicuro che ci sia un handle di eventi per questo, quindi dovresti conservare una copia del DOM e confrontarlo con il DOM esistente in una sorta di ciclo di polling - che potrebbe essere abbastanza brutto. Firebug lo fa però: se si controlla l'HTML e si eseguono alcune modifiche DOM, evidenzia le modifiche in giallo nella console Firebug per un breve periodo.

In alternativa, è possibile creare una funzione di rimozione ...

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

1
+1 per questa idea. Anche se potresti anche estendere jQuery (stile plugin) per ottenere una chiamata jQuery più standard come: $ ('. ItemToRemove'). CustomRemove () ;. Potresti anche farlo in modo che accetti un callback come parametro.
user113716

1

Ecco come creare un listener di rimozione live di jQuery :

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

O:

$(document).on('DOMNodeRemoved', function(e)
{
  $(e.target).find('.element').each(function()
  {
    // do anything with $(this)
  }
});

1
Sarebbe bello essere liberi di farlo. Purtroppo non è affidabile, poiché gli eventi di mutazione sono stati deprecati: developer.mozilla.org/en-US/docs/Web/Guide/Events/…
Kamafeather,

1

L'evento "rimuovi" da jQuery funziona perfettamente, senza aggiunta. Potrebbe essere più affidabile in tempo per usare un semplice trucco, invece di applicare patch a jQuery.

Modifica o aggiungi un attributo nell'elemento che stai per rimuovere dal DOM. Pertanto, è possibile attivare qualsiasi funzione di aggiornamento, che ignorerà semplicemente gli elementi sulla strada da distruggere, con l'attributo "do_not_count_it".

Supponiamo di avere una tabella con celle corrispondenti ai prezzi e che sia necessario mostrare solo l'ultimo prezzo: questo è il selettore da attivare quando viene eliminata una cella di prezzo (abbiamo un pulsante in ogni riga della tabella che lo fa, non mostrato Qui)

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

Ed ecco una funzione che trova l'ultimo prezzo nella tabella, non tenendo conto dell'ultimo, se fosse quello che è stato rimosso. Infatti, quando viene attivato l'evento "rimuovi" e quando viene chiamata questa funzione, l'elemento non viene ancora rimosso.

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

Alla fine, la funzione update_prices () funziona correttamente e, successivamente, l'elemento DOM viene rimosso.


0

riferendosi alla risposta @David:

Quando vuoi farlo con un'altra funzione, ad es. html () come nel mio caso, non dimenticare di aggiungere return in una nuova funzione:

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

0

Questo.

$.each(
  $('#some-element'), 
        function(i, item){
            item.addEventListener('DOMNodeRemovedFromDocument',
                function(e){ console.log('I has been removed'); console.log(e);
                })
         })

1
DOMNodeRemovedFromDocument sembra non essere supportato in Firefox. Forse prova invece MutationObserver ?
EricP,

0

un'estensione della risposta di Adam nel caso in cui sia necessario prevalere sul valore predefinito, ecco una soluzione:

$(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);
        }
    });

0

Possiamo anche usare DOMNodeRemoved:

$("#youridwhichremoved").on("DOMNodeRemoved", function () {
// do stuff
})
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.