jQuery - dispara um evento quando um elemento é removido do DOM

211

Estou tentando descobrir como executar algum código js quando um elemento é removido da página:

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

existe um evento feito para isso, algo como:

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

obrigado.

sa125
fonte
1
por curiosidade, o que gostaria de fazer com o elemento que foi removido?
Natrium
8
Eu tenho um elemento que se anexa independentemente à peça que removo, então quero detectar quando essa peça se foi para eliminar esse elemento também. Eu poderia redesenhar a coisa toda, mas realizar o que foi mencionado acima economizará muito tempo (e código).
precisa saber é

Respostas:

118

Apenas verificado, ele já está embutido na versão atual do JQuery:

jQuery - v1.9.1

UI do jQuery - v1.10.2

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

Importante : Essa é a funcionalidade do script da interface do usuário do Jquery (não do JQuery), portanto, você deve carregar os dois scripts (jquery e jquery-ui) para fazê-lo funcionar. Aqui está um exemplo: http://jsfiddle.net/72RTz/

Philipp Munin
fonte
11
Isso foi muito útil. Aprendi que essa funcionalidade está dentro do componente "Widget" da jQuery UI, se você não deseja fazer o download de toda a biblioteca da UI.
Neil
5
Isso está documentado em algum lugar? Eu apenas examinei a documentação do widget da interface do usuário do jQuery e não consegui encontrar uma menção a isso. Gostaria de ver se este é oficialmente suportado / quaisquer advertências sobre seu uso ...
josh
Este não funciona - tentou em jQuery 1.10.2, no entanto a resposta abaixo por @mtkopone funciona perfeitamente, portanto, eu votaria para atualizar a resposta a essa pergunta
Marcin
20
Este funciona apenas parcialmente. Se você executar removeo elemento, ele dispara, mas se o elemento for destruído de alguma outra maneira, sendo sobrescrito, digamos, não funcionará.
22613 Carl Smith
Você pode estar certo, provavelmente existe outro nome de evento para este caso. Seria ótimo se você pudesse fornecer uma amostra do jsfiddle para o seu caso.
Philipp Munin
195

Você pode usar eventos especiais do jQuery para isso.

Com toda a simplicidade,

Configuração:

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

Uso:

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

Adendo para responder aos comentários de Pierre e DesignerGuy:

Para não ativar o retorno de chamada durante a chamada $('.thing').off('destroyed'), altere a condição if para:if (o.handler && o.type !== 'destroyed') { ... }

mtkopone
fonte
15
Solução muito boa. Ben Alman tem uma boa redação sobre eventos especiais para obter mais informações: benalman.com/news/2010/03/jquery-special-events
antti_s
11
+1 conseguiu perder completamente esses eventos especiais até agora, muito útil! Uma coisa a declarar que acabou de implementar o acima, seria melhor mudar o.handler()para o.handler.apply(this,arguments)o evento e os objetos de dados não passarem pelo ouvinte de evento.
Pebbl
6
Isso não funciona quando você remove elementos sem o jQuery.
djjeck
5
isso não funciona a) quando os elementos são destacados em vez de removido ou b) quando alguns velhos bibliotecas não-jQuery usar innerHTML para destruir seus elementos (semelhante ao que djjeck disse)
Charon ME
5
Cuidado que manipulador é chamado quando $('.thing').unbind('destroyed')o que poderia realmente ser irritante (desde meios UNBIND não queremos que o manipulador a ser chamado ...)
Pierre
54

Você pode vincular ao evento DOMNodeRemoved (parte da especificação WC3 do nível 3 do DOM).

Funciona no IE9, versões mais recentes do Firefox e Chrome.

Exemplo:

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

Você também pode receber uma notificação quando elementos são inseridos vinculando-os a DOMNodeInserted

Adão
fonte
13
Nota: "Adicionar ouvintes de mutação DOM a um documento prejudica profundamente o desempenho de outras modificações DOM nesse documento (tornando-as 1,5 a 7 vezes mais lentas!)." de: developer.mozilla.org/en/DOM/Mutation_events
Matt Crinklaw-Vogt
1
Adoro isso, embora o nodeName raramente seja útil para mim. Eu apenas uso e.target.classNameou if ($(e.target).hasClass('my-class')) { ....
Micah Alcorn
Isso não está funcionando no próprio elemento, apenas em seus filhos.
xdg
Resposta incrível! Realmente me ajudou!
precisa
Pode ser lento e uma péssima idéia para o código na produção, mas esse é o único método que funciona de forma síncrona (ao contrário do MutationObserver) e para qualquer nó (não apenas os nós removidos via jQuery). Essa é uma ótima opção para depuração.
CertainPerformance
37

Não há evento interno para remover elementos, mas você pode criar um usando o método de remoção padrão do jQuery de extensão falsa. Observe que o retorno de chamada deve ser chamado antes de removê-lo para manter a referência.

(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: alguns problemas com esta resposta foram descritos por outros pôsteres.

  1. Isso não funcionará quando o nó for removido via html() replace()ou outros métodos jQuery
  2. Este evento borbulha
  3. As substituições de UI do jQuery também removem

A solução mais elegante para esse problema parece ser: https://stackoverflow.com/a/10172676/216941

David Hellsing
fonte
2
Obrigado por isso! Um pouco disso: porque o remove-evento borbulha, você também iria recebê-lo quando uma criança é removido, então é melhor escrever o manipulador desse jeito:$('#some-element').bind('remove', function(ev) { if (ev.target === this) { console.log('removed!'); } });
meyertee
3
Isso não funcionou imediatamente para mim - tive que retornar o resultado do orig.apply.
fturtle
1
Na verdade, é @ Adam, mas é compatível com vários navegadores. Com as adições de meyertee / fturtle, é uma solução perfeitamente confiável, desde que você remova apenas elementos com esse método, em vez de modificar / esvaziar o HTML etc. você deve estar atento a eventos de negócios em um aplicativo, não aos DOM cuja estrutura provavelmente será alterada com o desenvolvimento. Além disso, a inscrição em eventos de mutação DOM significa que seu programa é potencialmente suscetível a atrasos em hierarquias DOM complexas.
Will Morgan,
1
Minha única avaliação da precisão é a afirmação - 'não existe evento interno para remover elementos' - existe um evento interno para navegadores que implementam eventos DOM de nível 3 (conforme detalhado na minha resposta).
Adam
4
Embora isso detecte elementos removidos usando a função 'remover', ele falhará na detecção de elementos removidos por outros meios (por exemplo, usando o html, substituir, etc) do jQuery. Por favor, veja minha resposta para uma solução mais completa.
Zah
32

Hooking .remove()não é a melhor maneira de lidar com isso, pois há muitas maneiras para remover elementos da página (por exemplo, usando .html(), .replace(), etc).

Para evitar vários riscos de vazamento de memória, o jQuery internamente tentará chamar a função jQuery.cleanData()para cada elemento removido, independentemente do método usado para removê-lo.

Consulte esta resposta para obter mais detalhes: vazamentos de memória javascript

Portanto, para obter melhores resultados, você deve conectar a cleanDatafunção, que é exatamente o que o plugin jquery.event.destroyed faz:

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

zah
fonte
Essa visão cleanDatafoi muito útil para mim! Muito obrigado, Joe :)
Petr Vostrel
1
Boa resposta, mas ainda assim isso só funciona para métodos jQuery. Se você está se integrando a outra plataforma que pode "puxar o tapete" de baixo de você - a resposta de Adam faz mais sentido.
Bron Davies
7

Para quem usa jQuery UI:

jQuery UI tem substituído alguns dos métodos do jQuery para implementar um removeevento que será tratado não só quando você remover explicitamente o elemento dado, mas também se o elemento é removido do DOM por quaisquer métodos jQuery auto-limpeza (por exemplo replace, html, etc.) . Isso basicamente permite que você coloque um gancho nos mesmos eventos que são acionados quando o jQuery está "limpando" os eventos e dados associados a um elemento DOM.

John Resig indicou que está aberto à ideia de implementar este evento em uma versão futura do núcleo do jQuery, mas não tenho certeza de onde ele está atualmente.

StriplingWarrior
fonte
6

Somente o jQuery é necessário (nenhuma interface do usuário do jQuery é necessária)

( Eu extraí esta extensão da estrutura da interface do usuário do jQuery )

Funciona com: 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 );

Com esta solução, você também pode desvincular o manipulador de eventos.

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

Tente! - Exemplo

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

Demonstração Adicional - jsBin

Legendas
fonte
4

Não consegui que esta resposta funcionasse com a desobstrução (apesar da atualização, veja aqui ), mas consegui descobrir uma maneira de contorná-la. A resposta foi criar um evento especial 'destroy_proxy' que acionou um evento 'destruído'. Você coloca o ouvinte de evento em 'destroyed_proxy' e 'destroyed', e quando você deseja desvincular, basta desassociar o evento 'destroyed':

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

Aqui está um violino

Bill Johnston
fonte
E a compatibilidade do navegador aqui? Ainda funciona?
Vladislav Rastrusny 9/09/16
3

Eu gosto da resposta do mtkopone usando eventos especiais do jQuery, mas observe que ele não funciona a) quando elementos são desanexados em vez de removidos ou b) quando algumas bibliotecas antigas não-jquery usam innerHTML para destruir seus elementos

Charon ME
fonte
3
Fiz uma votação baixa porque a pergunta solicitada explicitamente pelo uso de .remove (), não desanexou. detaché usado especialmente para não acionar a limpeza, pois provavelmente o elemento está planejado para ser anexado mais tarde. b) ainda é verdadeiro e ainda não possui tratamento confiável, pelo menos desde que os eventos de mutação do DOM sejam amplamente implementados.
21613 Pierre Pierre
4
Aposto que você fez isso apenas para obter o emblema "crítico";)
Charon ME
1

Não tenho certeza de que exista um identificador de evento para isso, então você teria que manter uma cópia do DOM e comparar com o DOM existente em algum tipo de loop de pesquisa - o que poderia ser bastante desagradável. O Firebug faz isso - se você inspeciona o HTML e executa algumas alterações no DOM, destaca as alterações em amarelo no console do Firebug por um curto período de tempo.

Como alternativa, você pode criar uma função de remoção ...

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
fonte
1
+1 para esta ideia. Embora você também possa estender o jQuery (estilo de plug-in) para obter uma chamada jQuery mais padrão, como: $ ('. ItemToRemove'). CustomRemove () ;. Você também pode fazer com que ele aceite um retorno de chamada como parâmetro.
precisa saber é o seguinte
1

Isto é como criar um ouvinte de remoção ao vivo do jQuery :

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

Ou:

$(document).on('DOMNodeRemoved', function(e)
{
  $(e.target).find('.element').each(function()
  {
    // do anything with $(this)
  }
});
Buzogany Laszlo
fonte
1
Seria ótimo ser livre para fazer isso. Infelizmente, não é confiável, como a mutação-eventos estão sendo removidos: developer.mozilla.org/en-US/docs/Web/Guide/Events/...
Kamafeather
1

O evento "remove" do jQuery funciona bem, sem adição. Pode ser mais confiável na hora de usar um truque simples, em vez de aplicar o patch do jQuery.

Basta modificar ou adicionar um atributo no elemento que você está prestes a remover do DOM. Assim, você pode acionar qualquer função de atualização, que apenas ignorará os elementos a serem destruídos, com o atributo "do_not_count_it".

Suponha que tenhamos uma tabela com células correspondentes aos preços e que você precise mostrar apenas o último preço: este é o seletor a ser acionado quando uma célula de preço for excluída (temos um botão em cada linha da tabela, não mostrado aqui)

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

E aqui está uma função que encontra o último preço na tabela, sem levar em conta o último, se foi o que foi removido. De fato, quando o evento "remover" é acionado e quando essa função é chamada, o elemento ainda não foi removido.

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

No final, a função update_prices () funciona bem e, depois disso, o elemento DOM é removido.

philippe
fonte
0

referenciando a resposta @ David:

Quando você deseja fazer isso com outra função, por exemplo. html () como no meu caso, não se esqueça de adicionar retorno na nova função:

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

Este.

$.each(
  $('#some-element'), 
        function(i, item){
            item.addEventListener('DOMNodeRemovedFromDocument',
                function(e){ console.log('I has been removed'); console.log(e);
                })
         })
Matas Vaitkevicius
fonte
1
DOMNodeRemovedFromDocument parece não ser suportado no Firefox. Talvez tente MutationObserver ?
EricP
0

uma extensão da resposta de Adam, caso você precise prever o padrão, aqui está uma solução alternativa:

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

Também podemos usar DOMNodeRemoved:

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