jQuery - Sự kiện kích hoạt khi một phần tử bị xóa khỏi DOM


211

Tôi đang cố gắng tìm ra cách thực thi một số mã js khi một phần tử bị xóa khỏi trang:

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

Có một sự kiện phù hợp cho điều đó, đại loại như:

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

cảm ơn.


1
Vì tò mò, những gì sẽ muốn làm với các yếu tố đã được gỡ bỏ?
Natrium

8
Tôi có một phần tử độc lập tự gắn vào phần tôi loại bỏ, vì vậy tôi muốn phát hiện khi phần đó biến mất để loại bỏ phần tử đó. Tôi có thể thiết kế lại toàn bộ, nhưng hoàn thành những điều trên sẽ giúp tôi tiết kiệm rất nhiều thời gian (và mã).
sa125

Câu trả lời:


118

Chỉ cần kiểm tra, nó đã được tích hợp sẵn trong phiên bản hiện tại của JQuery:

jQuery - v1.9.1

Giao diện người dùng jQuery - v1.10.2

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

Quan trọng : Đây là chức năng của tập lệnh UI Jquery (không phải JQuery), vì vậy bạn phải tải cả hai tập lệnh (jquery và jquery-ui) để làm cho nó hoạt động. Dưới đây là ví dụ: http://jsfiddle.net/72RTz/


11
Điều này rất hữu ích. Tôi đã học được rằng chức năng này nằm trong thành phần "Widget" của Giao diện người dùng jQuery nếu bạn không muốn tải xuống toàn bộ thư viện UI.
Neil

5
Đây có phải là tài liệu ở bất cứ đâu? Tôi chỉ xem qua tài liệu tiện ích UI của jQuery và không thể tìm thấy đề cập nào về điều này. Muốn xem liệu điều này có được hỗ trợ chính thức / có bất kỳ cảnh báo nào về việc sử dụng nó không ...
josh

Cái này không hoạt động - đã thử trong jQuery 1.10.2, tuy nhiên câu trả lời dưới đây của @mtkopone hoạt động hoàn hảo do đó tôi sẽ bỏ phiếu để cập nhật câu trả lời trong câu hỏi đó
Marcin

20
Điều này chỉ một phần hoạt động. Nếu bạn thực hiện removetrên phần tử mà nó kích hoạt, nhưng nếu phần tử bị phá hủy theo cách khác, bằng cách ghi đè, nó sẽ không hoạt động.
Carl Smith

Bạn có thể đúng, có lẽ có tên sự kiện khác cho trường hợp này. Sẽ thật tuyệt nếu bạn có thể cung cấp mẫu jsfiddle cho trường hợp của bạn.
Philipp Munin

195

Bạn có thể sử dụng các sự kiện đặc biệt của jQuery cho việc này.

Trong tất cả sự đơn giản,

Thiết lập:

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

Sử dụng:

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

Phụ lục để trả lời ý kiến ​​của Pierre và DesignerGuy:

Để không có cuộc gọi lại khi gọi $('.thing').off('destroyed'), hãy thay đổi điều kiện if thành:if (o.handler && o.type !== 'destroyed') { ... }


15
Giải pháp thực sự tốt đẹp. Ben Alman có một bài viết hay về các sự kiện đặc biệt để biết thêm thông tin: benalman.com/news/2010/03/jquery-special-events
antti_s

11
+1 đã hoàn toàn bỏ lỡ những sự kiện đặc biệt này cho đến bây giờ, thật hữu ích! Một điều cần nói là vừa thực hiện những điều trên, tốt nhất nên đổi o.handler()sang mặt o.handler.apply(this,arguments)khác, các đối tượng dữ liệu và sự kiện không được truyền qua người nghe sự kiện.
Pebbl

6
Tuy nhiên, điều này không hoạt động khi bạn loại bỏ các phần tử không có jQuery.
djjeck

5
điều này không hoạt động a) khi các phần tử bị tách ra thay vì bị xóa hoặc b) khi một số thư viện không phải là jquery cũ sử dụng InternalHTML để phá hủy các phần tử của bạn (tương tự như những gì djjeck đã nói)
Charon ME

5
Coi chừng người xử lý được gọi khi bạn $('.thing').unbind('destroyed')thực sự có thể gây phiền nhiễu (vì không ràng buộc nghĩa là chúng tôi không muốn người xử lý được gọi ...)
Pierre

54

Bạn có thể liên kết với sự kiện DOMNodeRemond (một phần của thông số DOM3 Cấp 3 WC3).

Hoạt động trong IE9, phiên bản mới nhất của Firefox và Chrome.

Thí dụ:

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

Bạn cũng có thể nhận thông báo khi các phần tử được chèn bằng cách liên kết với DOMNodeInserted


13
Lưu ý: "Thêm trình nghe đột biến DOM vào tài liệu làm giảm đáng kể hiệu suất của các sửa đổi DOM tiếp theo đối với tài liệu đó (làm cho chúng chậm hơn 1,5 - 7 lần!)." từ: developer.mozilla.org/en/DOM/Muting_events
Matt Crinklaw-Vogt

1
Tình yêu này, mặc dù nodeName hiếm khi hữu ích cho tôi. Tôi chỉ sử dụng e.target.classNamehoặc if ($(e.target).hasClass('my-class')) { ....
Micah Alcorn

Điều này không hoạt động trên chính yếu tố, chỉ có con của nó.
Xdg

Câu trả lời tuyệt vời! Thực sự đã giúp tôi!
Kir Mazur

Nó có thể chậm và là một ý tưởng tồi cho mã trong sản xuất, nhưng đây có vẻ như là phương pháp duy nhất hoạt động đồng bộ (không giống như MutingObserver) và cho bất kỳ nút nào (không chỉ các nút bị xóa qua jQuery). Đây là một lựa chọn tuyệt vời để gỡ lỗi.
SurePerformance 20/03/19

37

Không có sự kiện tích hợp nào để xóa các phần tử, nhưng bạn có thể tạo một phần tử bằng phương thức loại bỏ mặc định của jQuery. Lưu ý rằng gọi lại phải được gọi trước khi thực sự loại bỏ nó để giữ tham chiếu.

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

Lưu ý: một số vấn đề với câu trả lời này đã được phác thảo bởi các áp phích khác.

  1. Điều này sẽ không hoạt động khi nút được gỡ bỏ thông qua html() replace()hoặc các phương thức jQuery khác
  2. Sự kiện này nổi lên
  3. Giao diện người dùng jQuery cũng ghi đè loại bỏ

Giải pháp thanh lịch nhất cho vấn đề này dường như là: https://stackoverflow.com/a/10172676/216941


2
Cảm ơn vì điều đó! Một bổ sung nhỏ: vì sự kiện loại bỏ bong bóng, bạn cũng sẽ nhận được nó khi một đứa trẻ bị xóa, vì vậy tốt hơn nên viết trình xử lý theo cách đó:$('#some-element').bind('remove', function(ev) { if (ev.target === this) { console.log('removed!'); } });
meyertee

3
Điều này không phù hợp với tôi - tôi đã phải trả lại kết quả từ orig.apply.
fturtle

1
Trên thực tế, @Adam, đúng vậy, nhưng nó tương thích trình duyệt chéo. Với các bổ sung của meyertee / fturtle, đây là một giải pháp hoàn toàn đáng tin cậy, miễn là bạn chỉ xóa các phần tử bằng phương pháp này, thay vì sửa đổi / làm trống HTML, v.v ... Đối với một cái gì đó linh hoạt hơn, có các sự kiện đột biến DOM là tốt nhưng tôi đã nghi ngờ về chúng như bạn nên lắng nghe các sự kiện kinh doanh trong một ứng dụng, không phải các sự kiện DOM có cấu trúc có thể thay đổi với sự phát triển hơn nữa. Ngoài ra, đăng ký các sự kiện đột biến DOM có nghĩa là chương trình của bạn có khả năng dễ bị tụt lại trong hệ thống phân cấp DOM phức tạp.
Will Morgan

1
Sự đồng thuận duy nhất của tôi về độ chính xác là tuyên bố - 'không có sự kiện nào được xây dựng để loại bỏ các yếu tố' --- có một sự kiện được xây dựng cho các trình duyệt thực hiện các sự kiện DOM cấp 3 (như chi tiết trong câu trả lời của tôi).
Adam

4
Mặc dù điều này sẽ phát hiện các phần tử bị loại bỏ bằng cách sử dụng chức năng 'remove', nhưng nó sẽ không phát hiện ra các phần tử bị loại bỏ bởi các phương tiện khác (ví dụ: sử dụng html, thay thế, v.v. của jQuery). Xin vui lòng, xem câu trả lời của tôi cho giải pháp đầy đủ hơn.
zah

32

Hooking .remove()không phải là cách tốt nhất để xử lý việc này như có rất nhiều cách để loại bỏ các yếu tố từ trang (ví dụ bằng cách sử dụng .html(), .replace(), vv).

Để ngăn chặn các nguy cơ rò rỉ bộ nhớ khác nhau, bên trong jQuery sẽ cố gắng gọi hàm jQuery.cleanData()cho từng phần tử bị loại bỏ bất kể phương thức được sử dụng để loại bỏ nó.

Xem câu trả lời này để biết thêm chi tiết: rò rỉ bộ nhớ javascript

Vì vậy, để có kết quả tốt nhất, bạn nên kết nối cleanDatahàm, đó chính xác là những gì mà plugin jquery.event.destroyed thực hiện:

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


Cái nhìn sâu sắc này cleanDatalà rất hữu ích cho tôi! Cảm ơn bạn rất nhiều, Joe :)
Petr Vostrel

1
Câu trả lời hay, nhưng vẫn chỉ hoạt động đối với các phương thức jQuery. Nếu bạn đang tích hợp với một nền tảng khác có thể "kéo tấm thảm ra" từ bên dưới bạn - câu trả lời của Adam có ý nghĩa nhất.
Bron Davies

7

Đối với những người sử dụng jQuery UI:

jQuery UI đã ghi đè một số phương pháp jQuery để thực hiện một removetrường hợp bị xử lý không chỉ khi bạn loại bỏ một cách rõ ràng các yếu tố nhất định, mà còn nếu nguyên tố này sẽ bị xoá khỏi DOM bằng bất kỳ phương pháp jQuery tự làm sạch (ví dụ replace, htmlvv) . Điều này về cơ bản cho phép bạn đặt một hook vào cùng các sự kiện được kích hoạt khi jQuery đang "dọn dẹp" các sự kiện và dữ liệu được liên kết với một phần tử DOM.

John Resig đã chỉ ra rằng anh ấy cởi mở với ý tưởng triển khai sự kiện này trong phiên bản tương lai của lõi jQuery, nhưng tôi không chắc nó hiện đang đứng ở đâu.


6

Chỉ yêu cầu jQuery (Không cần UI jQuery)

( Tôi đã trích xuất phần mở rộng này từ khung UI UI )

Hoạt động với: 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 );

Với giải pháp này, bạn cũng có thể hủy liên kết xử lý sự kiện.

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

Thử nó! - Thí dụ

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

Bản demo bổ sung - jsBin


4

Tôi không thể có câu trả lời này để làm việc với unbinding (mặc dù bản cập nhật xem ở đây ), nhưng đã có thể tìm ra cách xung quanh nó. Câu trả lời là tạo ra một sự kiện đặc biệt 'hủy_proxy' đã kích hoạt sự kiện 'bị phá hủy'. Bạn đặt trình lắng nghe sự kiện trên cả 'Bị hủy_proxy' và 'bị hủy', sau đó khi bạn muốn hủy liên kết, bạn chỉ cần hủy liên kết sự kiện 'bị hủy':

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

Đây là một câu đố


Điều gì về khả năng tương thích trình duyệt ở đây? Nó vẫn hoạt động?
Vladislav Rastrusny

3

Tôi thích câu trả lời của mtkopone khi sử dụng các sự kiện đặc biệt của jQuery, nhưng lưu ý rằng nó không hoạt động a) khi các phần tử được tách ra thay vì loại bỏ hoặc b) khi một số thư viện không phải là jquery cũ sử dụng InternalHTML để phá hủy các phần tử của bạn


3
Tôi đã bỏ qua vì câu hỏi được hỏi rõ ràng về việc sử dụng .remove (), không tách ra. detachđược sử dụng đặc biệt để không kích hoạt dọn dẹp vì phần tử có thể được lên kế hoạch để được gắn lại sau này. b) vẫn đúng và chưa có xử lý đáng tin cậy, ít nhất là do các sự kiện đột biến DOM được triển khai rộng rãi.
Pierre

4
Tôi cá là bạn đã làm điều này chỉ để có được huy hiệu "nhà phê bình";)
Charon ME

1

Tôi không chắc chắn có một xử lý sự kiện cho việc này, vì vậy bạn sẽ phải giữ một bản sao của DOM và so sánh với DOM hiện có trong một số vòng lặp bỏ phiếu - điều này có thể khá khó chịu. Firebird thực hiện điều này mặc dù - nếu bạn kiểm tra HTML và chạy một số thay đổi DOM, nó sẽ làm nổi bật các thay đổi màu vàng trong bảng điều khiển Fireorms trong một thời gian ngắn.

Ngoài ra, bạn có thể tạo chức năng xóa ...

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 cho ý tưởng này. Mặc dù bạn cũng có thể mở rộng jQuery (kiểu plugin) để có được một cuộc gọi jQuery tiêu chuẩn hơn như: $ ('. ItemToRemove'). CustomRemove ();. Bạn cũng có thể làm cho nó để nó chấp nhận gọi lại làm tham số.
dùng113716

1

Đây là cách tạo trình nghe loại bỏ trực tiếp jQuery :

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

Hoặc là:

$(document).on('DOMNodeRemoved', function(e)
{
  $(e.target).find('.element').each(function()
  {
    // do anything with $(this)
  }
});

1
Nó sẽ là tuyệt vời để được tự do để làm điều đó. Thật không may, nó không đáng tin cậy, vì các sự kiện đột biến đang bị phản đối: developer.mozilla.org/en-US/docs/Web/Guide/Events/
phỏng

1

Sự kiện "xóa" khỏi jQuery hoạt động tốt mà không cần thêm. Có thể đáng tin cậy hơn trong thời gian sử dụng một thủ thuật đơn giản, thay vì vá jQuery.

Chỉ cần sửa đổi hoặc thêm một thuộc tính trong thành phần bạn sắp xóa khỏi DOM. Do đó, bạn có thể kích hoạt bất kỳ chức năng cập nhật nào, sẽ bỏ qua các phần tử trên đường bị hủy, với thuộc tính "do_not_count_it".

Giả sử chúng ta có một bảng có các ô tương ứng với giá và bạn chỉ cần hiển thị giá cuối cùng: Đây là công cụ chọn để kích hoạt khi một ô giá bị xóa (chúng tôi có một nút trong mỗi dòng của bảng làm việc đó, không hiển thị đây)

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

Và đây là một hàm tìm giá cuối cùng trong bảng, không tính đến giá cuối cùng, nếu đó là hàm đã bị xóa. Thật vậy, khi sự kiện "remove" được kích hoạt và khi hàm này được gọi, phần tử vẫn chưa bị xóa.

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

Cuối cùng, hàm update_prices () hoạt động tốt và sau đó, phần tử DOM được loại bỏ.


0

tham khảo câu trả lời @David:

Khi bạn muốn làm soo với chức năng khác, vd. html () như trong trường hợp của tôi, đừng quên thêm return trong hàm mới:

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

0

Điều này.

$.each(
  $('#some-element'), 
        function(i, item){
            item.addEventListener('DOMNodeRemovedFromDocument',
                function(e){ console.log('I has been removed'); console.log(e);
                })
         })

1
DOMNodeRemondFromDocument dường như không được hỗ trợ trong Firefox. Có lẽ nên thử M mutObserver thay thế?
EricP

0

một phần mở rộng cho câu trả lời của Adam trong trường hợp bạn cần phải trả trước mặc định, đây là một cách giải quyết:

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

Chúng tôi cũng có thể sử dụng DOMNodeRemond:

$("#youridwhichremoved").on("DOMNodeRemoved", function () {
// do stuff
})
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.