jQuery - Zdarzenie wyzwalające, gdy element jest usuwany z DOM

211

Próbuję dowiedzieć się, jak wykonać kod js, gdy element zostanie usunięty ze strony:

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

czy istnieje wydarzenie dostosowane do tego, na przykład:

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

dzięki.

sa125
źródło
1
z ciekawości, co chciałby zrobić z usuniętym elementem?
Natrium
8
Mam element, który samodzielnie przyczepia się do elementu, który usuwam, więc chcę wykryć, kiedy ten element zniknie, aby wyeliminować również ten element. Mógłbym przeprojektować całość, ale wykonanie powyższego pozwoli mi zaoszczędzić dużo czasu (i kodu).
sa125

Odpowiedzi:

118

Właśnie sprawdzone, jest już wbudowane w bieżącą wersję JQuery:

jQuery - v1.9.1

jQuery UI - v1.10.2

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

Ważne : Jest to funkcjonalność skryptu interfejsu użytkownika Jquery (nie JQuery), więc aby załadować oba skrypty (jquery i jquery-ui), należy go załadować. Oto przykład: http://jsfiddle.net/72RTz/

Philipp Munin
źródło
11
To było bardzo pomocne. Dowiedziałem się, że ta funkcja jest zawarta w komponencie „Widget” interfejsu użytkownika jQuery, jeśli nie chcesz pobierać całej biblioteki interfejsu użytkownika.
Neil
5
Czy jest to gdziekolwiek udokumentowane? Właśnie przejrzałem dokumentację widżetu interfejsu użytkownika jQuery i nie mogłem znaleźć wzmianki o tym. Chciałbym sprawdzić, czy jest to oficjalnie obsługiwane / czy są jakieś zastrzeżenia dotyczące korzystania z niego ...
Jos
Ten nie działa - wypróbowany w jQuery 1.10.2, jednak odpowiedź poniżej autorstwa @mtkopone działa idealnie, dlatego głosowałbym za aktualizacją odpowiedzi w tym pytaniu
Marcin,
20
Ten tylko częściowo działa. Jeśli zrobisz to removena elemencie, zostanie on uruchomiony, ale jeśli element zostanie zniszczony w inny sposób, po nadpisaniu powiedz, że nie działa.
Carl Smith
Być może masz rację, prawdopodobnie dla tej sprawy istnieje inna nazwa zdarzenia. Byłoby wspaniale, gdybyś mógł podać próbkę jsfiddle dla swojego przypadku.
Philipp Munin
195

Możesz do tego użyć specjalnych wydarzeń jQuery .

W całej prostocie

Ustawiać:

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

Stosowanie:

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

Dodatek do odpowiedzi na komentarze Pierre'a i DesignerGuya:

Aby nie wywoływać wywołania zwrotnego podczas połączenia $('.thing').off('destroyed'), zmień warunek if na:if (o.handler && o.type !== 'destroyed') { ... }

mtkopone
źródło
15
Naprawdę fajne rozwiązanie. Ben Alman napisał fajną recenzję na temat wydarzeń specjalnych, aby uzyskać więcej informacji: benalman.com/news/2010/03/jquery-special-events
antti_s
11
+1 udało się do tej pory całkowicie przegapić te specjalne wydarzenia, do cholery przydatne! Jedną rzeczą do stwierdzenia po właśnie wdrożeniu powyższego, najlepiej byłoby zmienić, o.handler()aby w o.handler.apply(this,arguments)przeciwnym razie zdarzenia i obiekty danych nie byłyby przekazywane przez detektor zdarzeń.
Pebbl,
6
Jednak to nie działa, gdy usuwasz elementy bez jQuery.
djjeck
5
to nie działa a) gdy elementy są odłączane zamiast usuwane lub b) gdy niektóre stare biblioteki inne niż jquery używają innerHTML do niszczenia twoich elementów (podobnie do tego, co powiedział djjeck)
Charon ME
5
Strzeż się, że program obsługi jest wywoływany, gdy $('.thing').unbind('destroyed')może to być naprawdę denerwujące (ponieważ unbind oznacza, że ​​nie chcemy, aby program obsługiwał się ...)
Pierre
54

Możesz powiązać ze zdarzeniem DOMNodeRemoved (część specyfikacji DOM Level 3 WC3).

Działa w IE9, najnowszych wydaniach Firefox i Chrome.

Przykład:

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

Możesz również otrzymywać powiadomienia o wstawianiu elementów przez powiązanie z DOMNodeInserted

Adam
źródło
13
Uwaga: „Dodanie detektorów mutacji DOM do dokumentu znacznie obniża wydajność dalszych modyfikacji DOM w tym dokumencie (czyniąc je 1,5 - 7 razy wolniejszym!)”. from: developer.mozilla.org/en/DOM/Mutation_events
Matt Crinklaw-Vogt
1
Uwielbiam to, chociaż nodeName rzadko jest dla mnie przydatny. Po prostu używam e.target.classNamelub if ($(e.target).hasClass('my-class')) { ....
Micheasz Alcorn,
To nie działa na samym elemencie, tylko na jego elementach potomnych.
Xdg,
Niesamowita odpowiedź! Naprawdę mi pomógł!
Kir Mazur
Może to być powolny i zły pomysł na kod w produkcji, ale wygląda na to, że jest to jedyna metoda, która działa synchronicznie (w przeciwieństwie do MutationObserver) i dla dowolnego węzła (nie tylko węzłów usuniętych przez jQuery). To świetna opcja do debugowania.
CertainPerformance
38

Nie ma wbudowanego zdarzenia do usuwania elementów, ale możesz je utworzyć, stosując domyślną metodę usuwania fałszywego rozszerzenia jQuery. Pamiętaj, że wywołanie zwrotne musi zostać wywołane przed faktycznym usunięciem, aby zachować odniesienie.

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

Uwaga: niektóre problemy z tą odpowiedzią zostały nakreślone przez inne plakaty.

  1. To nie zadziała, gdy węzeł zostanie usunięty za pomocą html() replace()innych metod jQuery
  2. To wydarzenie pęka
  3. Usunięto również interfejs użytkownika jQuery

Najbardziej eleganckim rozwiązaniem tego problemu wydaje się być: https://stackoverflow.com/a/10172676/216941

David Hellsing
źródło
2
Dziękuję za to! Jeden mały dodatek: ponieważ zdarzenie remove pęcherzy się, otrzymasz je również, gdy dziecko zostanie usunięte, więc lepiej napisz w ten sposób $('#some-element').bind('remove', function(ev) { if (ev.target === this) { console.log('removed!'); } });
moduł
3
Nie wyszło mi to z pudełka - musiałem zwrócić wynik z orig.apply.
fturtle 18.07.11
1
Właściwie to @Adam, ale jest kompatybilny z wieloma przeglądarkami. Z dodatkami meyertee / fturtle jest to całkowicie niezawodne rozwiązanie, o ile usuwasz tylko elementy tą metodą, zamiast modyfikować / opróżniać HTML itp. Jeśli chodzi o coś bardziej elastycznego, tak, zdarzenia mutacji DOM są w porządku, ale jestem sceptyczny, ponieważ powinieneś słuchać wydarzeń biznesowych w aplikacji, a nie DOM, których struktura może ulec zmianie wraz z dalszym rozwojem. Subskrybowanie zdarzeń mutacji DOM oznacza również, że Twój program może być podatny na opóźnienia w złożonych hierarchiach DOM.
Will Morgan,
1
Moje jedyne rozbieżności co do dokładności to stwierdzenie - „nie ma wbudowanego zdarzenia do usuwania elementów” --- istnieje wbudowane zdarzenie dla przeglądarek, które implementują zdarzenia DOM poziomu 3 (jak szczegółowo opisano w mojej odpowiedzi).
Adam
4
Chociaż wykryje to elementy usunięte za pomocą funkcji „usuń”, nie będzie w stanie wykryć elementów usuniętych w inny sposób (np. Używając html, zamień itp. JQuery). Proszę zobaczyć moją odpowiedź na bardziej kompletne rozwiązanie.
zah
32

Przechwytywanie .remove()nie jest najlepszym sposobem na poradzenie sobie z tym, ponieważ istnieje wiele sposobów usuwania elementów ze strony (np. Przy użyciu .html(),.replace() itp).

Aby zapobiec różnym zagrożeniom wycieku pamięci, wewnętrznie jQuery spróbuje wywołać funkcję jQuery.cleanData()dla każdego usuniętego elementu, niezależnie od metody użytej do jego usunięcia.

Aby uzyskać więcej informacji, zobacz tę odpowiedź: wycieki pamięci javascript

Tak więc, aby uzyskać najlepsze wyniki, powinieneś podpiąć cleanDatafunkcję, która właśnie robi wtyczka jquery.event.destroyed :

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

zah
źródło
Ten wgląd cleanDatabył dla mnie bardzo pomocny! Dziękuję bardzo, Joe :)
Petr Vostrel
1
Ładna odpowiedź, ale nadal działa to tylko w przypadku metod jQuery. Jeśli integrujesz się z inną platformą, która może „wyciągnąć dywan” spod ciebie - odpowiedź Adama jest najbardziej sensowna.
Bron Davies
7

Dla tych, którzy używają interfejsu użytkownika jQuery:

jQuery UI jest przesłonięta niektóre z metod jQuery zaimplementować removezdarzenie, które zostanie obsłużony nie tylko wtedy, gdy wyraźnie usunąć dany element, ale również wtedy, gdy element zostanie usunięty z DOM żadnymi samoczyszczących metod jQuery (np replace,html itp) . Zasadniczo pozwala to na zaczepienie tych samych zdarzeń, które są uruchamiane, gdy jQuery „oczyszcza” zdarzenia i dane powiązane z elementem DOM.

John Resig wskazał , że jest otwarty na pomysł zaimplementowania tego wydarzenia w przyszłej wersji rdzenia jQuery, ale nie jestem pewien, gdzie obecnie się znajduje.

StriplingWarrior
źródło
6

Wymagane jest tylko jQuery (nie wymaga interfejsu użytkownika jQuery)

( Wyodrębniłem to rozszerzenie ze środowiska interfejsu użytkownika jQuery )

Działa z: empty()i html()iremove()

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

Dzięki temu rozwiązaniu możesz również odznaczyć moduł obsługi zdarzeń.

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

Spróbuj! - przykład

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

Dodatkowe demo - jsBin

Legendy
źródło
4

Nie udało mi się uzyskać tej odpowiedzi do rozpakowania (pomimo aktualizacji, patrz tutaj ), ale udało mi się to rozwiązać. Odpowiedzią było utworzenie zdarzenia specjalnego „destroy_proxy”, które wywołało zdarzenie „zniszczone”. Umieszczasz detektor zdarzeń zarówno na „zniszczone_proxy”, jak i „zniszczone”, a następnie, gdy chcesz cofnąć powiązanie, po prostu cofnij powiązanie zdarzenia „zniszczone”:

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

Oto skrzypce

Bill Johnston
źródło
Co z kompatybilnością przeglądarki tutaj? Czy to nadal działa?
Vladislav Rastrusny
3

Podoba mi się odpowiedź mtkopone przy użyciu specjalnych zdarzeń jQuery, ale zauważ, że to nie działa a) gdy elementy są odłączone zamiast usuwane lub b) gdy niektóre stare biblioteki nie jquery używają innerHTML do niszczenia twoich elementów

Charon ME
źródło
3
Zgłosiłem się negatywnie, ponieważ pytanie wyraźnie dotyczyło użycia .remove (), a nie odłączenia. detachjest używany szczególnie, aby nie uruchamiać czyszczenia, ponieważ prawdopodobnie planowane jest ponowne dołączenie elementu później. b) jest nadal prawdą i nie ma jeszcze niezawodnej obsługi, przynajmniej odkąd zdarzenia mutacji DOM mogą być szeroko wdrażane.
Pierre
4
Założę się, że zrobiłeś to tylko po to, aby zdobyć odznakę „krytyka”;)
Charon ME
1

Nie jestem pewien, czy istnieje do tego uchwyt zdarzenia, więc musiałbyś zachować kopię DOM i porównać ją z istniejącą DOM w jakiejś pętli odpytywania - co może być dość nieprzyjemne. Firebug robi to jednak - jeśli przejrzysz HTML i wykonasz kilka zmian DOM, na krótki czas podświetli zmiany na żółto w konsoli Firebug.

Alternatywnie możesz utworzyć funkcję usuwania ...

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
źródło
1
+1 za ten pomysł. Chociaż możesz także rozszerzyć jQuery (styl wtyczki), aby uzyskać bardziej standardowe wywołanie jQuery, takie jak: $ ('. ItemToRemove'). CustomRemove () ;. Możesz także ustawić go tak, aby akceptował oddzwonienie jako parametr.
user113716
1

Oto jak utworzyć detektor jQuery na żywo :

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

Lub:

$(document).on('DOMNodeRemoved', function(e)
{
  $(e.target).find('.element').each(function()
  {
    // do anything with $(this)
  }
});
Buzogany Laszlo
źródło
1
Byłoby wspaniale mieć taką możliwość. Niestety nie jest to wiarygodne, ponieważ zdarzenia mutacji są przestarzałe: developer.mozilla.org/en-US/docs/Web/Guide/Events/…
Kamafeather
1

Zdarzenie „remove” z jQuery działa dobrze, bez dodawania. Może być bardziej niezawodne w czasie, aby zastosować prostą sztuczkę, zamiast łatania jQuery.

Po prostu zmodyfikuj lub dodaj atrybut w elemencie, który chcesz usunąć z DOM. W ten sposób można uruchomić dowolną funkcję aktualizacji, która po prostu zignoruje elementy na drodze do zniszczenia, z atrybutem „do_not_count_it”.

Załóżmy, że mamy tabelę z komórkami odpowiadającymi cenom i że musisz pokazać tylko ostatnią cenę: Jest to selektor uruchamiany po usunięciu komórki ceny (mamy przycisk w każdym wierszu tabeli, który to pokazuje, nie pokazano tutaj)

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

A tutaj jest funkcja, która znajduje ostatnią cenę w tabeli, nie biorąc pod uwagę ostatniej, jeśli to ta, która została usunięta. Rzeczywiście, po uruchomieniu zdarzenia „remove” i wywołaniu tej funkcji element nie jest jeszcze usuwany.

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

Na koniec funkcja update_prices () działa dobrze, a następnie element DOM jest usuwany.

Filip
źródło
0

odnosząc się do odpowiedzi @David:

Gdy chcesz to zrobić za pomocą innej funkcji, np. html () jak w moim przypadku, nie zapomnij dodać return w nowej funkcji:

(function() {
    var ev = new $.Event('html'),
        orig = $.fn.html;
    $.fn.html = function() {
        $(this).trigger(ev);
        return orig.apply(this, arguments);
    }
})();
Patryk M.
źródło
0

To.

$.each(
  $('#some-element'), 
        function(i, item){
            item.addEventListener('DOMNodeRemovedFromDocument',
                function(e){ console.log('I has been removed'); console.log(e);
                })
         })
Matas Vaitkevicius
źródło
1
DOMNodeRemovedFromDocument wydaje się nie być obsługiwany w przeglądarce Firefox. Może zamiast tego spróbuj MutationObserver ?
EricP
0

rozszerzenie odpowiedzi Adama, ponieważ musisz uprzedzić domyślną, oto obejście:

$(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
źródło
0

Możemy również użyć DOMNodeRemoved:

$("#youridwhichremoved").on("DOMNodeRemoved", function () {
// do stuff
})
Den Pat
źródło