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

Дякую.


1
з цікавості, що б хотіли зробити з елементом, який було видалено?
Натрій

8
У мене є елемент, який самостійно прикріплюється до фрагмента, який я виймаю, тому я хочу виявити, коли цей шматок піде, щоб усунути цей елемент. Я міг би переробити всю справу, але виконання вищезгаданого заощадить мені багато часу (та коду).
sa125

Відповіді:


118

Щойно перевірено, він уже вбудований у поточну версію JQuery:

jQuery - v1.9.1

jQuery UI - v1.10.2

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

Важливо : Це функціональність сценарію інтерфейсу Jquery (не 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
})

Додаток до відповіді на коментарі П'єра та ДизайнераГью:

Щоб під час виклику не було заготівлі зворотного дзвінка $('.thing').off('destroyed'), змініть умову if на:if (o.handler && o.type !== 'destroyed') { ... }


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, використовують InternalHTML для знищення елементів (подібно до того, що сказав djjeck)
Charon ME

5
Слідкуйте за тим, щоб вас обробляли, коли ви $('.thing').unbind('destroyed')насправді можете дратувати (оскільки розв’язування означає, що ми не хочемо, щоб обробник називався ...)
П'єр

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/uk/DOM/Mutation_events
Метт Крінклав-Фогт

1
Любіть це, хоча nodeName для мене рідко корисний. Я просто використовую e.target.classNameабо if ($(e.target).hasClass('my-class')) { ....
Міхей Алькорн

Це не працює над самим елементом, тільки його дітьми.
Xdg

Дивовижна відповідь! Дійсно мені допомогли!
Кір Мазур

Це може бути повільним і поганою ідеєю для коду у виробництві, але це схоже на єдиний метод, який працює синхронно (на відміну від MutationObserver) і для будь-якого вузла (не лише вузлів, видалених через jQuery). Це чудовий варіант для налагодження.
Окреме виконання

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 інтерфейсу користувача також видаляється

Найбільш елегантним рішенням цієї проблеми, здається, є: 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
Хоча це буде виявляти елементи, видалені за допомогою функції "видалити", вона не зможе виявити елементи, видалені іншими способами (наприклад, використовуючи jQuery в HTML, заміни тощо). Будь ласка, дивіться мою відповідь для більш повного рішення.
Захід

32

Зачеплення .remove() не найкращий спосіб впоратися з цим , оскільки є багато способів видалення елементів зі сторінки (наприклад , за допомогою .html(),.replace() і т.д.).

Щоб запобігти різним небезпекам витоку пам'яті, внутрішньо jQuery спробує викликати функцію jQuery.cleanData()для кожного видаленого елемента незалежно від методу, який використовується для його видалення.

Докладнішу інформацію див. У цій відповіді: витоки пам'яті javascript

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

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


Це розуміння про cleanDataмене дуже допомогло! Дуже дякую, Джо :)
Петро Вострель

1
Приємна відповідь, але все ще працює лише для методів jQuery. Якщо ви інтегруєтесь з іншою платформою, яка може "витягнути килим" з-під вас - відповідь Адама має найбільш сенс.
Брон Девіс

7

Для тих, хто використовує інтерфейс jQuery:

JQuery UI має перевизначений деякі методи JQuery реалізувати removeподія , яке отримує не обробляється тільки тоді , коли ви явно видалити даний елемент, але якщо елемент отримує видалений з DOM будь-яких самоочищаються методів Jquery (наприклад replace, htmlі т.д.) . Це в основному дозволяє помістити гачок на ті самі події, які звільняються, коли jQuery "прибирає" події та дані, пов'язані з елементом DOM.

Джон Резіг зазначив, що відкритий до ідеї впровадження цієї події у майбутній версії ядра jQuery, але я не впевнений, де вона зараз стоїть.


6

Необхідний лише jQuery (не потрібен інтерфейс jQuery)

( Я витягнув це розширення з фреймворку 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

Я не зміг отримати цю відповідь для роботи з розв’язуванням (незважаючи на оновлення, див. Тут ), але зміг розібратися у цьому способі. Відповідь полягала в створенні спеціальної події "знищити_проксі", яка викликала "знищену" подію. Ви ставите слухача події як на "знищену_проксі", так і на "знищену", тоді, коли ви хочете від’єднати, просто відв'яжіть подію "знищена":

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, використовують InternalHTML для знищення ваших елементів


3
Я заявив, що це питання явно задається для використання .remove (), а не відключення. detachвикористовується спеціально, щоб не викликати очищення, оскільки, ймовірно, планується повторний приєднання елемента пізніше. б) як і раніше, правда і досі не має надійного поводження, принаймні, оскільки події мутації DOM широко застосовуються.
П’єр

4
Б'юсь об заклад, ви зробили це лише для того, щоб отримати знак "критик";)
Харон ME

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 () ;. Ви також можете зробити так, щоб він приймав зворотний зв'язок як параметр.
користувач113716

1

Ось як створити слухача видалення jQuery в реальному часі :

$(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:

Коли ви хочете зробити ду з іншою функцією, наприклад. 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);
        }
    });

0

Ми також можемо використовувати DOMNodeRemoved:

$("#youridwhichremoved").on("DOMNodeRemoved", function () {
// do stuff
})
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.