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
出于好奇,该如何处理已删除的元素?
Natrium

8
我有一个元素独立地独立于要删除的部分,因此我想检测该部分何时消失以消除该部分。我可以重新设计整个过程,但是完成以上操作将为我节省大量时间(和代码)。
2010年

Answers:


118

刚刚检查,它已经内置在当前版本的JQuery中:

jQuery-v1.9.1

jQuery UI-v1.10.2

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

重要说明:这是Jquery UI脚本(不是JQuery)的功能,因此您必须同时加载两个脚本(jquery和jquery-ui)才能使其正常工作。例如:http : //jsfiddle.net/72RTz/


11
这非常有帮助。我了解到,如果您不想下载整个UI库,则此功能位于jQuery UI的“窗口小部件”组件中。
尼尔

5
这在任何地方都有记录吗?我只是浏览了jQuery UI窗口小部件文档,却找不到相关内容。希望了解是否得到正式支持/有关使用它的任何警告...
2013年

这个不起作用-在jQuery 1.10.2中尝试过,但是下面@mtkopone的答案非常有效,因此我投票赞成更新该问题的答案
Marcin

20
这仅部分起作用。如果remove对元素执行操作,它将触发,但是如果元素以其他方式被破坏(例如被覆盖),则该元素将不起作用。
卡尔·史密斯,

您可能是对的,在这种情况下可能还有其他事件名称。如果可以为您的案例提供jsfiddle示例,那就太好了。
Philipp Munin

195

您可以为此使用jQuery特殊事件

简单来说,

建立:

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

用法:

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

回答Pierre和DesignerGuy的评论的附录:

要在调用时没有触发回调$('.thing').off('destroyed'),请将if条件更改为:if (o.handler && o.type !== 'destroyed') { ... }


15
真的很好的解决方案。本·阿尔曼(Ben Alman)撰写了
antti_s 2012年

11
到目前为止,+ 1已完全错过了这些特殊事件,真是有用!需要说明的一件事是刚刚实现了上述内容,否则最好更改o.handler()o.handler.apply(this,arguments)否则事件和数据对象不会通过事件侦听器传递。
Pebbl 2012年

6
但是,当您删除不带jQuery的元素时,这将不起作用。
djjeck

5
这不起作用a)当元素被分离而不是被移除时,或者b)当一些旧的非jquery库使用innerHTML破坏您的元素时(类似于djjeck所说的)
Charon ME

5
当您$('.thing').unbind('destroyed')可能真的很烦时,请注意会调用处理程序(因为取消绑定意味着我们不希望调用该处理程序...)
Pierre

54

您可以绑定到DOMNodeRemoved事件(DOM Level 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
Matt Crinklaw-Vogt 2012年

1
喜欢这个,尽管nodeName对我很少有用。我只是使用e.target.classNameif ($(e.target).hasClass('my-class')) { ...
米卡·阿尔康

这不适用于元素本身,仅适用于其子元素。
Xdg '16

惊人的答案!真的帮了我!
Kir Mazur

对于生产中的代码来说,这可能很慢并且是个坏主意,但这似乎是唯一可以同步工作的方法(与MutationObserver不同),并且可以对任何节点(不仅是通过jQuery删除的节点同步工作。这是调试的绝佳选择。
某些性能

37

没有用于删除元素的内置事件,但是您可以通过假扩展jQuery的默认remove方法来创建一个事件。请注意,必须在实际删除回调之前先调用该回调以保持引用。

(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也会覆盖remove

解决此问题的最佳方法似乎是:https : //stackoverflow.com/a/10172676/216941


2
感谢那!一点补充:由于remove-event冒泡,当孩子被删除时,您也会收到它,所以最好这样写处理程序:$('#some-element').bind('remove', function(ev) { if (ev.target === this) { console.log('removed!'); } });
meyertee 2011年

3
这对我来说不是开箱即用的-我不得不从orig.apply返回结果。
fturtle 2011年

1
实际上,@ Adam是的,但是它与跨浏览器兼容。有了meyertee / fturtle的添加,这是一个非常可靠的解决方案,只要您仅使用此方法删除元素,而不是修改/清空HTML等即可。对于更灵活的方法,可以使用DOM突变事件,但我一直对此表示怀疑您应该在应用程序中监听业务事件,而不是在结构可能会随着进一步开发而改变的DOM中监听。另外,订阅DOM突变事件意味着您的程序可能容易陷入复杂的DOM层次结构中。
威尔·摩根(Morgan)

1
我对准确性的唯一看法是-“没有内置事件可删除元素” ---对于实现3级DOM事件的浏览器有一个内置事件(如我的回答中所述)。
亚当

4
虽然这将检测使用“删除”功能删除的元素,但将无法检测通过其他方式删除的元素(例如,使用jQuery的html,replace等)。请,请参阅我的答案以获取更完整的解决方案。
2011年

32

挂钩.remove()是不处理的最佳方式,因为有许多方法可以删除页面元素(例如,通过使用.html().replace()等等)。

为了防止发生各种内存泄漏危险,jQuery内部将尝试jQuery.cleanData()为每个已删除元素调用函数,而不管用于删除它的方法如何。

有关更多详细信息,请参见此答案:javascript内存泄漏

因此,为了获得最佳结果,您应该挂钩该cleanData函数,这正是jquery.event.destroyed插件的作用:

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


关于此的见解 cleanData我非常有帮助!乔,非常感谢您:)
Petr Vostrel 2012年

1
好的答案,但仍然只适用于jQuery方法。如果您正在与另一个可以从下面“拉出地毯”的平台集成,那么Adam的回答最有意义。
布朗·戴维斯

7

对于使用jQuery UI的用户:

jQuery UI的重写了一些jQuery方法来实现remove这不仅受到处理的事件,当你明确移除给定的元素,而且如果该元素被从DOM任何自洁jQuery方法移除(例如replacehtml等) 。基本上,这使您可以将与jQuery“清除”与DOM元素关联的事件和数据时触发的事件挂钩。

John Resig表示他愿意在将来的jQuery core版本中实现此事件,但我不确定当前的状态。


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

我无法获得解决绑定问题的答案(尽管更新见此处),但能够找到解决方法。答案是创建一个触发“ destroyed”事件的特殊事件“ destroy_proxy”。您将事件侦听器同时置于“ destroyed_proxy”和“ destroyed”上,然后当您想要解除绑定时,只需解除对“ 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++;
});

这是一个小提琴


这里的浏览器兼容性如何?它仍然有效吗?
弗拉迪斯拉夫·拉斯特鲁尼(Fladislav Rastrusny),2013年

3

我喜欢mtkopone使用jQuery特殊事件的答案,但请注意a)当元素被分离而不是被移除时不起作用,或者b)当一些旧的非jquery库使用innerHTML破坏您的元素时,它不起作用


3
我之所以投票,是因为该问题明确要求使用.remove()而不是分离。detach专门用于不触发清理,因为该元素可能计划在以后重新连接。b)仍然是正确的,并且还没有可靠的处理方法,至少由于DOM突变事件得到了广泛实施。
皮埃尔

4
我敢打赌,你这样做只是为了获得“评论家”徽章;)
Charon 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();。您也可以使其接受回调作为参数。
user113716 2010年

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答案:

当您想使用其他功能(例如)时。html()就我而言,不要忘记在新函数中添加return:

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


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
})
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.