jQuery - запускает событие, когда элемент удаляется из DOM

211

Я пытаюсь выяснить, как выполнить код JS, когда элемент удаляется со страницы:

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

есть ли для этого событие, что-то вроде:

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

Спасибо.

sa125
источник
1
из любопытства, что бы хотелось сделать с элементом, который был удален?
Natrium
8
У меня есть элемент, который самостоятельно прикрепляется к удаляемой частице, поэтому я хочу определить, когда эта часть исчезла, чтобы устранить этот элемент. Я мог бы перепроектировать все это, но выполнение вышесказанного сэкономит мне много времени (и кода).
sa125

Ответы:

118

Только что проверил, он уже встроен в текущую версию JQuery:

JQuery - v1.9.1

Пользовательский интерфейс jQuery - v1.10.2

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

Важное замечание : Это функциональность сценария Jquery UI (не JQuery), поэтому для его работы необходимо загрузить оба сценария (jquery и jquery-ui). Вот пример: http://jsfiddle.net/72RTz/

Филипп Мунин
источник
11
Это было очень полезно. Я узнал, что эта функция находится в компоненте «Виджет» пользовательского интерфейса jQuery, если вы не хотите загружать всю библиотеку пользовательского интерфейса.
Нейл
5
Это где-нибудь задокументировано? Я только что просмотрел документацию по виджетам пользовательского интерфейса jQuery и не смог найти упоминания об этом. Хотелось бы узнать, поддерживается ли это официально / какие-либо предостережения по поводу его использования ...
Джош
Этот не работает - пробовал в jQuery 1.10.2, однако ответ ниже @mtkopone работает отлично, поэтому я бы проголосовал за обновление ответа на этот вопрос
Marcin
20
Этот только частично работает. Если вы делаете removeна элементе, он срабатывает, но если элемент уничтожается другим способом, то есть, если он перезаписан, скажем, он не работает.
Карл Смит
Возможно, вы правы, возможно, для этого случая есть другое название события. Было бы здорово, если бы вы могли предоставить пример jsfiddle для вашего случая.
Филипп Мунин
195

Вы можете использовать специальные события jQuery .

Во всей простоте,

Настроить:

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

Использование:

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

Приложение к комментариям Пьера и DesignerGuy:

Чтобы не вызывать обратный вызов при вызове $('.thing').off('destroyed'), измените условие if на:if (o.handler && o.type !== 'destroyed') { ... }

mtkopone
источник
15
Действительно хорошее решение. У Бен Алмана есть хорошая статья о специальных мероприятиях для получения дополнительной информации: benalman.com/news/2010/03/jquery-special-events
antti_s
11
+1 до сих пор удавалось полностью пропустить эти особые события, чертовски полезно! Одна вещь, которую нужно заявить, только что реализовав вышеизложенное, было бы лучше изменить o.handler()на o.handler.apply(this,arguments)иначе, поскольку объекты события и данные не будут переданы через прослушиватель событий.
Pebbl
6
Это не работает, когда вы удаляете элементы без jQuery.
djjeck
5
это не работает а) когда элементы отсоединяются, а не удаляются, или б) когда некоторые старые не-jquery библиотеки используют innerHTML для уничтожения ваших элементов (аналогично тому, что сказал djjeck)
Charon ME
5
Остерегайтесь того, что этот обработчик вызывается, когда вы $('.thing').unbind('destroyed')действительно раздражаете (поскольку отмена привязки означает, что мы не хотим, чтобы вызывался обработчик ...)
Pierre
54

Вы можете привязаться к событию DOMNodeRemoved (часть спецификации DOM уровня 3 WC3).

Работает в IE9, последних версиях Firefox и Chrome.

Пример:

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

Вы также можете получать уведомления при вставке элементов, связываясь с DOMNodeInserted

Адам
источник
13
Примечание: «Добавление прослушивателей DOM-мутаций в документ значительно снижает производительность дальнейших модификаций DOM в этом документе (делая их в 1,5–7 раз медленнее!)». От: developer.mozilla.org/en/DOM/Mutation_events
Мэтт Кринкло-Фогт
1
Люблю это, хотя nodeName редко бывает полезным для меня. Я просто использую e.target.classNameили if ($(e.target).hasClass('my-class')) { ....
Мика Олкорн
Это работает не над самим элементом, а только над его дочерними элементами.
Xdg
Удивительный ответ! Действительно помог мне!
Кир Мазур
Это может быть медленным и плохая идея для кода в производстве, но это похоже на единственный метод, который работает синхронно (в отличие от MutationObserver) и для любого узла (не только узлов, удаленных через jQuery). Это отличный вариант для отладки.
CertainPerformance
38

Нет встроенного события для удаления элементов, но вы можете создать его, используя метод удаления по умолчанию, выдвинутый поддельным расширением jQuery. Обратите внимание, что обратный вызов должен быть вызван до фактического удаления, чтобы сохранить ссылку.

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

Примечание: некоторые проблемы с этим ответом были изложены другими авторами.

  1. Это не будет работать, когда узел удаляется с помощью html() replace()или других методов jQuery
  2. Это событие вспыхивает
  3. JQuery UI перезаписывает также удалить

Наиболее элегантное решение этой проблемы выглядит следующим образом: https://stackoverflow.com/a/10172676/216941.

Дэвид Хеллсинг
источник
2
Спасибо за это! Одно небольшое дополнение: поскольку событие удаления всплывает, вы также получите его, когда ребенок будет удален, поэтому лучше напишите обработчик таким образом:$('#some-element').bind('remove', function(ev) { if (ev.target === this) { console.log('removed!'); } });
meyertee
3
Это не сработало из коробки - мне пришлось вернуть результат из orig.apply.
fturtle
1
На самом деле, @Adam, это так, но он совместим с разными браузерами. С добавлением meyertee / fturtle это совершенно надежное решение, если вы удаляете элементы только этим методом, а не модифицируете / очищаете HTML и т. Д. Для чего-то более гибкого, да, мутационные события DOM хороши, но я скептически относился к ним как вы должны прислушиваться к бизнес-событиям в приложении, а не к DOM, чья структура может измениться при дальнейшем развитии. Кроме того, подписка на события мутации DOM означает, что ваша программа потенциально подвержена отставанию в сложных иерархиях DOM.
Уилл Морган
1
Мое единственное согласие в отношении точности - утверждение: «нет встроенного события для удаления элементов» - есть встроенное событие для браузеров, которые реализуют события DOM уровня 3 (как подробно описано в моем ответе).
Адам
4
Хотя это будет определять элементы, удаленные с помощью функции «удалить», оно не сможет обнаружить элементы, удаленные другими способами (например, с помощью hQml, замены и т. Д. В jQuery). Пожалуйста, смотрите мой ответ для более полного решения.
Зах
32

Подключаем .remove()не самый лучший способ справиться с этим , поскольку есть много способов удаления элементов со страницы (например , с помощью .html(),.replace() и т.д.).

Чтобы предотвратить различные угрозы утечки памяти, внутренне jQuery попытается вызвать функцию jQuery.cleanData() для каждого удаленного элемента независимо от метода, используемого для его удаления.

Смотрите этот ответ для более подробной информации: утечки памяти javascript

Таким образом, для достижения наилучших результатов, вы должны подключить cleanDataфункцию, которая является именно тем, что jquery.event.destroyed плагин :

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

Zah
источник
Это понимание cleanDataбыло очень полезно для меня! Большое спасибо, Джо :)
Петр Вострел
1
Хороший ответ, но все же это работает только для методов jQuery. Если вы интегрируетесь с другой платформой, которая может «вытащить коврик» из-под вас - ответ Адама имеет смысл.
Брон Дэвис
7

Для тех, кто использует JQuery UI:

Пользовательский интерфейс jQuery переопределил некоторые методы jQuery для реализации removeсобытия, которое обрабатывается не только при явном удалении данного элемента, но также и при удалении элемента из DOM с помощью любых самоочищающихся методов jQuery (например replace, htmlи т. д.) , Это в основном позволяет вам подключить те же события, которые запускаются, когда jQuery «очищает» события и данные, связанные с элементом DOM.

Джон Резиг указал, что он открыт для идеи реализации этого события в будущей версии ядра jQuery, но я не уверен, где оно сейчас стоит.

StriplingWarrior
источник
6

Требуется только jQuery (не требуется jQuery UI)

( Я извлек это расширение из инфраструктуры jQuery UI )

Работает с: empty()и html()иremove()

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

С этим решением вы также можете отменить привязку обработчика событий.

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

Попытайся! - Пример

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

Дополнительная демоверсия - jsBin

Легенды
источник
4

Я не мог заставить этот ответ работать с отменой привязки (несмотря на обновление, см. Здесь ), но смог найти способ обойти это. Ответ состоял в том, чтобы создать специальное событие destroy_proxy, которое вызвало бы уничтоженное событие. Вы помещаете прослушиватель событий в «destroy_proxy» и «destroy», а затем, когда вы хотите отменить привязку, вы просто отсоединяете «destroy» событие:

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

Вот скрипка

Билл Джонстон
источник
Как насчет совместимости браузера здесь? Это все еще работает?
Владислав Раструсный
3

Мне нравится ответ mtkopone, использующий специальные события jQuery, но учтите, что он не работает а) когда элементы отсоединяются вместо удаления, или б) когда некоторые старые не-jquery библиотеки используют innerHTML для уничтожения ваших элементов

Харон ME
источник
3
Я понизил голосование, потому что вопрос задан явно для использования .remove (), а не detach. detachособенно используется, чтобы не запускать очистку, так как элемент, вероятно, планируется подключить позже. b) все еще верно и еще не имеет надежной обработки, по крайней мере, так как события мутации DOM будут широко реализованы.
Пьер
4
Могу поспорить, что вы сделали это только для того, чтобы получить значок «критик»;)
Харон М.Е.
1

Я не уверен, что для этого есть дескриптор события, поэтому вам нужно будет сохранить копию DOM и сравнить с существующей DOM в каком-то цикле опроса - что может быть довольно неприятно. Firebug делает это, хотя - если вы проверяете HTML и запускаете некоторые DOM-изменения, он выделяет изменения желтым цветом в консоли Firebug на короткое время.

Кроме того, вы можете создать функцию удаления ...

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 за эту идею. Хотя вы также можете расширить jQuery (стиль плагина), чтобы получить более стандартный вызов jQuery, такой как: $ ('. ItemToRemove'). CustomRemove () ;. Вы также можете сделать так, чтобы он принимал обратный вызов в качестве параметра.
user113716
1

Вот как создать слушатель jQuery live remove :

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

Или:

$(document).on('DOMNodeRemoved', function(e)
{
  $(e.target).find('.element').each(function()
  {
    // do anything with $(this)
  }
});
Бузоганы Ласло
источник
1
Было бы здорово быть свободным сделать это. К сожалению, это ненадежно, поскольку мутационные события устарели: developer.mozilla.org/en-US/docs/Web/Guide/Events/…
Kamafeather
1

Событие "удалить" из jQuery работает нормально, без добавления. Возможно, будет более надежным вовремя использовать простой трюк вместо исправления jQuery.

Просто измените или добавьте атрибут в элемент, который вы собираетесь удалить из DOM. Таким образом, вы можете вызвать любую функцию обновления, которая будет просто игнорировать элементы на пути к уничтожению, с атрибутом "do_not_count_it".

Предположим, у нас есть таблица с ячейками, соответствующими ценам, и вам нужно показать только последнюю цену: это селектор, который срабатывает при удалении ценовой ячейки (у нас есть кнопка в каждой строке таблицы, которая делает это, не показано) Вот)

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

А вот функция, которая находит последнюю цену в таблице, не учитывая последнюю, если она была удалена. Действительно, когда срабатывает событие «удалить» и когда вызывается эта функция, элемент еще не удален.

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

В конце концов, функция update_prices () работает нормально, и после этого элемент DOM удаляется.

Филипп
источник
0

ссылаясь на ответ @David:

Когда вы хотите сделать soo с другой функцией, например. html () как в моем случае, не забудьте добавить return в новую функцию:

(function() {
    var ev = new $.Event('html'),
        orig = $.fn.html;
    $.fn.html = function() {
        $(this).trigger(ev);
        return orig.apply(this, arguments);
    }
})();
Патрик М
источник
0

Это.

$.each(
  $('#some-element'), 
        function(i, item){
            item.addEventListener('DOMNodeRemovedFromDocument',
                function(e){ console.log('I has been removed'); console.log(e);
                })
         })
Матас Вайткявичюс
источник
1
Кажется, DOMNodeRemovedFromDocument не поддерживается в Firefox. Возможно, вместо этого попробуйте MutationObserver ?
EricP
0

Расширение ответа Адама, в котором вам нужно заранее указать дефолт, вот обходной путь:

$(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
источник
0

Мы также можем использовать DOMNodeRemoved:

$("#youridwhichremoved").on("DOMNodeRemoved", function () {
// do stuff
})
Ден Пэт
источник