div phát hiện jquery của một lớp nhất định đã được thêm vào DOM


90

Tôi đang sử dụng .on()để liên kết các sự kiện của div được tạo sau khi tải trang. Nó hoạt động tốt cho nhấp chuột, trung tâm chuột ... nhưng tôi cần biết khi một div mới của lớp MyClass được thêm vào. Tôi đang tìm kiếm cái này:

$('#MyContainer').on({

  wascreated: function () { DoSomething($(this)); }

}, '.MyClass');

Làm thế nào để tôi làm điều này? Tôi đã quản lý để viết toàn bộ ứng dụng của mình mà không có plugin và tôi muốn giữ nó theo cách đó.

Cảm ơn.


Có phải mã của riêng bạn đang tạo ra các phần tử không? Nếu vậy, bạn có thể kiểm tra tại thời điểm bạn tạo nó và hành động cho phù hợp.
Những giấc mơ siêu thực

Đúng, điều đó đúng nhưng Với .on (), tôi có thể liên kết các sự kiện tại document.ready và sau đó tôi không phải lo lắng về ràng buộc khi tạo HTML. Tôi đang tìm kiếm hành vi tương tự với DoSomething.
frenchie

bạn có thể sử dụng $ ("div / your-selector"). hasClass ("MyClass"); để kiểm tra xem một div có lớp cụ thể hay không. Tôi đoán bạn có thể sử dụng api này.
KBN

.on () chỉ dành cho các sự kiện. Bạn có thể thiết lập một sự kiện tùy chỉnh, nhưng bạn vẫn phải kích hoạt sự kiện đó khi bạn tạo các phần tử mới.
Những giấc mơ siêu thực vào

Câu trả lời:


102

Trước đây người ta có thể móc vào domManipphương thức của jQuery để bắt tất cả các thao tác jQuery dom và xem những phần tử nào được chèn vào, v.v. nhưng nhóm jQuery đã tắt điều đó trong jQuery 3.0+ vì nó thường không phải là một giải pháp tốt để kết nối vào các phương thức jQuery theo cách đó và họ ' đã thực hiện nó để domManipphương thức nội bộ không còn khả dụng bên ngoài mã jQuery lõi.

Sự kiện đột biến cũng không được dùng nữa, như trước đây người ta có thể làm một số việc như

$(document).on('DOMNodeInserted', function(e) {
    if ( $(e.target).hasClass('MyClass') ) {
       //element with .MyClass was inserted.
    }
});

điều này nên tránh và ngày nay nên sử dụng Trình quan sát đột biến để thay thế, sẽ hoạt động như thế này

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        console.log(mutation)
        if (mutation.addedNodes && mutation.addedNodes.length > 0) {
            // element added to DOM
            var hasClass = [].some.call(mutation.addedNodes, function(el) {
                return el.classList.contains('MyClass')
            });
            if (hasClass) {
                // element has class `MyClass`
                console.log('element ".MyClass" added');
            }
        }
    });
});

var config = {
    attributes: true,
    childList: true,
    characterData: true
};

observer.observe(document.body, config);

Firefox và webkit khá ổn, nhưng hỗ trợ cho việc này hơi kém trong IE. Nó sẽ hoạt động trong IE9, nhưng có lẽ không quá nhiều trong IE8. Tôi biết tôi đã thấy các cách giải quyết, và tôi chưa thực sự kiểm tra điều này trong IE, vì vậy hãy kiểm tra nó trước, nếu nó không hoạt động thì hãy xem có cách giải quyết nào không. Có một plugin có vẻ như nó bổ sung hỗ trợ IE ở đây , nhưng tôi chưa thử, chỉ tìm thấy nó trong một tìm kiếm của Google.
adeneo

2
Ban đầu tôi đã sử dụng câu trả lời của bạn nhưng thực tế là nó không hoạt động trên nhiều trình duyệt là một vấn đề. Sau đó, tôi nhanh chóng tìm ra giải pháp để tránh vấn đề này ngay từ đầu. Và sau đó, vài tuần trở lại đây, vấn đề phát hiện chèn nút này lại xuất hiện trở lại trong mã của tôi. Lần này, mã mà tôi đã đăng trong câu trả lời của mình hoạt động CHÍNH XÁC như nó cần, trình duyệt chéo và không có lỗi gì. Vì vậy, đó là lý do tại sao tôi đã chuyển câu trả lời của mình như được chấp nhận; đó là cách SO hoạt động, OP quyết định câu trả lời nào anh ấy chấp nhận và đám đông bỏ phiếu cho câu trả lời. Theo thời gian, mọi người sẽ bình chọn và quyết định câu trả lời nào phù hợp với họ nhất.
frenchie

2
Ngoài ra, tôi muốn nói rằng bạn đã giúp tôi nhiều lần với những câu trả lời chất lượng cao đã cứu rỗi một ngày của tôi. Thay đổi câu trả lời được chấp nhận thành của tôi hoàn toàn dựa trên mã và cách nó phù hợp với vấn đề tôi phải giải quyết.
frenchie

có "của router Lỗi Loại: Không thể đọc thuộc 'chứa' không xác định" với mã này
Gordie

53

Đây là plugin của tôi làm được điều đó - jquery.initialize

Useage giống như cách bạn sử dụng .eachhàm, nhưng với .initializehàm trên phần tử, điểm khác biệt .eachlà nó cũng sẽ khởi tạo các phần tử được thêm vào trong tương lai mà không cần bất kỳ mã bổ sung nào - bất kể bạn thêm nó bằng AJAX hay bất kỳ thứ gì khác.

Khởi tạo có cú pháp hoàn toàn giống với hàm .each

$(".some-element").initialize( function(){
    $(this).css("color", "blue");
});

Nhưng bây giờ nếu phần tử mới khớp với .some-element selector xuất hiện trên trang, nó sẽ được khởi tạo ngay lập tức. Cách thêm mục mới không quan trọng, bạn không cần quan tâm đến bất kỳ lệnh gọi lại nào, v.v.

$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!

Plugin dựa trên MutationObserver


Cảm ơn bạn rất nhiều.
Brandon Harris

làm thế nào để dừng khởi tạo? Tôi muốn dừng lại xem một lần những thứ tôi đang tìm kiếm được thêm ...
Evgeny Vostok

$ (". some-element"). khởi tạo [..] không được dùng nữa. Sử dụng $ .initialize (". AddDialog", function () {$ ('. NoData'). Hide ();}); thay thế.
Heimi 22/03/18

Có thể thực hiện .initializehàm callback chỉ đối với các phần tử được thêm động (ví dụ: qua Ajax) và bỏ qua lệnh gọi lại cho các phần tử đã có khi trang tải ban đầu không? Tôi đang cố gắng thêm một tham số optionsvào để thực hiện điều này, nhưng cho đến nay tôi vẫn chưa làm được điều này.
Nevermore

Bạn có thể thêm vấn đề trên github về điều đó. Hiện tại, về mặt kỹ thuật, bạn có thể làm$('.myClass').addClass('ignore-initialize'); $.initialize('.myClass:not(.ignore-initialize)', callback);
pie6k

19

Sau khi xem lại bài đăng này và một số bài đăng khác, tôi đã cố gắng chắt lọc những gì tôi nghĩ là tốt nhất của mỗi bài thành một thứ đơn giản cho phép tôi phát hiện khi nào một lớp phần tử được chèn và sau đó hành động trên các phần tử đó .

function onElementInserted(containerSelector, elementSelector, callback) {

    var onMutationsObserved = function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.addedNodes.length) {
                var elements = $(mutation.addedNodes).find(elementSelector);
                for (var i = 0, len = elements.length; i < len; i++) {
                    callback(elements[i]);
                }
            }
        });
    };

    var target = $(containerSelector)[0];
    var config = { childList: true, subtree: true };
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    var observer = new MutationObserver(onMutationsObserved);    
    observer.observe(target, config);

}

onElementInserted('body', '.myTargetElement', function(element) {
    console.log(element);
});

Điều quan trọng đối với tôi, điều này cho phép a) phần tử đích tồn tại ở bất kỳ độ sâu nào trong "addedNodes" và b) khả năng xử lý phần tử chỉ khi được chèn ban đầu (không cần tìm kiếm toàn bộ cài đặt tài liệu hoặc bỏ qua "đã được xử lý" cờ).


1
Điều kiện của bạn là if(mutation.addedNodes.length)do addedNodesremovedNodescác mảng luôn ở trong mutationloại childList(được kiểm tra trong IE11, Chrome53, FF49). Vẫn là +1, vì câu trả lời của bạn là câu trả lời duy nhất addedNodesđược xem xét ( callbackchỉ được gọi khi một nút được thêm vào); không chỉ trên chủ đề này mà còn nhiều chủ đề khác. Mọi người khác chỉ gọi callbackcho tất cả các đột biến, ngay cả khi nút đang bị xóa.
Vivek Athalye

17

3 năm kinh nghiệm sau, đây là cách tôi lắng nghe "phần tử của một lớp nhất định được thêm vào DOM" : bạn chỉ cần thêm một hook vào html()hàm jQuery , như thế này:

function Start() {

   var OldHtml = window.jQuery.fn.html;

   window.jQuery.fn.html = function () {

     var EnhancedHtml = OldHtml.apply(this, arguments);

     if (arguments.length && EnhancedHtml.find('.MyClass').length) {

         var TheElementAdded = EnhancedHtml.find('.MyClass'); //there it is
     }

     return EnhancedHtml;
   }
}

$(Start);

Điều này hoạt động nếu bạn đang sử dụng jQuery, điều này tôi làm. Và nó không dựa vào sự kiện cụ thể của trình duyệt DOMNodeInserted, không tương thích với nhiều trình duyệt. Tôi cũng đã thêm cách triển khai tương tự cho.prepend()

Nhìn chung, điều này giống như một sự quyến rũ đối với tôi, và hy vọng đối với bạn cũng vậy.


6
Hmm - Tôi muốn làm rõ rằng điều này hoạt động nếu bạn chỉ sử dụng trình htmltrợ giúp của jQuery để sửa đổi DOM, không chỉ "sử dụng jQuery" nói chung. Các yếu tố được thêm bằng cách sử dụng, giả sử, jQuery.appendsẽ không kích hoạt sự kiện này.
iameli

1
Có, và đó là lý do tại sao tôi đã làm rõ rằng tôi đã thêm triển khai tương tự với .preprend () và nếu bạn sử dụng .append () thì hãy làm điều tương tự với .append ()
frenchie

Có một cách tiếp cận nhanh hơn nhiều, sử dụng hoạt ảnh CSS3 . Thư viện mã nguồn mở cũng cho vay để mã đơn giản hơn nhiều:insertionQ('#intercom-container').every(function(/element){ element.style.display = 'none'; });
Dan Dascalescu

3
Tại sao downvote ?? Điều này thực sự hoạt động: không có plugin và không có chức năng setTimeout hacky.
frenchie

2
Được ủng hộ - đây là một lựa chọn hợp lệ - tại sao trên thế giới (!!!) điều này lại bị phản đối ?????? Dan, CSS3 Animations không phải là XBC: caniuse.com/#search=keyframes ; davidwalsh.name/detect-node-insertion .
Cody

6

bạn có thể sử dụng các sự kiện đột biến

http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-mutationevents

BIÊN TẬP

từ MDN: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events

Không được dùng nữa Tính năng này đã bị xóa khỏi Web. Mặc dù một số trình duyệt có thể vẫn hỗ trợ nó, nhưng nó đang trong quá trình bị loại bỏ. Không sử dụng nó trong các dự án cũ hoặc mới. Các trang hoặc ứng dụng Web sử dụng nó có thể bị hỏng bất cứ lúc nào.

Trình quan sát đột biến là sự thay thế được đề xuất cho các sự kiện đột biến trong DOM4. Chúng sẽ được đưa vào Firefox 14 và Chrome 18.

https://developer.mozilla.org/en/docs/Web/API/MutationObserver

MutationObservercung cấp cho các nhà phát triển một cách để phản ứng với những thay đổi trong DOM. Nó được thiết kế để thay thế cho Sự kiện đột biến được xác định trong đặc tả Sự kiện DOM3.

Ví dụ sử dụng

Ví dụ sau được lấy từ http://hacks.mozilla.org/2012/05/dom-mutationobserver-reaction-to-dom-changes-without-killing-browser-performance/ .

// select the target node
var target = document.querySelector('#some-id');

// create an observer instance
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log(mutation.type);
  });    
});

// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };

// pass in the target node, as well as the observer options
observer.observe(target, config);

// later, you can stop observing
observer.disconnect();

2
Theo mdn , các sự kiện đột biến không còn được dùng nữa , thay vì MutationObservers
Olga

Bây giờ IE10 là EOL, nên gần như an toàn khi nói rằng MutationObserver được hỗ trợ rộng rãi.
rymo

0

Hai bổ sung cho câu trả lời của adeneo:

(1) chúng ta có thể thay đổi dòng

return el.classList.contains('MyClass');

Đến

if( el.classList ) {
    return el.classList.contains('MyClass');
} else {
    return false;
}

Vì vậy, chúng tôi sẽ không thấy "Uncaught TypeError: Cannot read property 'contains' of undefined"lỗi.

(2) thêm subtree: truevào configđể tìm các nút đã thêm một cách đệ quy trong tất cả các phần tử được thêm vào.

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.