เพิ่ม jquery ที่ตรวจจับ div ของคลาสบางคลาสใน DOM แล้ว


92

ฉันใช้.on()เพื่อเชื่อมโยงเหตุการณ์ของ div ที่สร้างขึ้นหลังจากโหลดเพจ ใช้งานได้ดีสำหรับการคลิก, mouseenter ... แต่ฉันต้องการทราบเมื่อมีการเพิ่ม Div ใหม่ของคลาส MyClass ฉันกำลังมองหาสิ่งนี้:

$('#MyContainer').on({

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

}, '.MyClass');

ฉันต้องทำอย่างไร ฉันสามารถเขียนแอปทั้งหมดได้โดยไม่ต้องใช้ปลั๊กอินและฉันต้องการให้มันเป็นแบบนั้น

ขอบคุณ.


เป็นรหัสของคุณเองที่สร้างองค์ประกอบหรือไม่ ในกรณีนี้คุณสามารถตรวจสอบได้ในขณะที่สร้างและดำเนินการตามนั้น
Surreal Dreams

ใช่นั่นเป็นความจริง แต่ด้วย. on () ฉันสามารถผูกเหตุการณ์ที่ document.ready ได้แล้วฉันก็ไม่ต้องกังวลเกี่ยวกับการเชื่อมโยงเมื่อฉันสร้าง HTML ฉันกำลังมองหาพฤติกรรมเดียวกันกับ DoSomething
เฟรนลี่

คุณสามารถใช้ $ ("div / your-selector"). hasClass ("MyClass"); เพื่อตรวจสอบว่า div มีคลาสเฉพาะหรือไม่ ฉันเดาว่าคุณสามารถนำ api นี้ไปใช้งานได้
KBN

.on () ใช้สำหรับเหตุการณ์เท่านั้น คุณสามารถตั้งค่าสำหรับเหตุการณ์ที่กำหนดเองได้ แต่คุณยังคงต้องทริกเกอร์เหตุการณ์นั้นเมื่อคุณสร้างองค์ประกอบใหม่
Surreal Dreams

คำตอบ:


105

ก่อนหน้านี้เราสามารถเชื่อมต่อdomManipวิธีการของ jQuery เพื่อจับการปรับแต่ง jQuery dom ทั้งหมดและดูว่ามีองค์ประกอบใดบ้างที่แทรก ฯลฯ แต่ทีม jQuery ปิดตัวลงใน jQuery 3.0+ เนื่องจากโดยทั่วไปแล้วไม่ใช่วิธีที่ดีในการเชื่อมต่อกับวิธีการ jQuery ด้วยวิธีนี้และพวกเขา ' ทำให้domManipวิธีการภายในไม่สามารถใช้ได้อีกต่อไปนอกรหัส jQuery หลัก

เหตุการณ์การกลายพันธุ์ได้ถูกเลิกใช้เช่นกันเนื่องจากก่อนหน้านี้เราสามารถทำบางสิ่งได้เช่น

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

ควรหลีกเลี่ยงสิ่งนี้และควรใช้ Mutation Observers ในปัจจุบันแทนซึ่งจะได้ผลเช่นนี้

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 และ webkit น่าจะโอเค แต่การรองรับนี้ค่อนข้างแย่ใน IE ควรทำงานใน IE9 แต่อาจไม่มากนักใน IE8 ฉันรู้ว่าฉันได้เห็นวิธีแก้ปัญหาแล้วและฉันไม่ได้ทดสอบสิ่งนี้ใน IE จริงๆดังนั้นให้ทดสอบก่อนหากยังไม่ได้ผลจากนั้นดูว่ามีวิธีแก้ปัญหาหรือไม่ มีปลั๊กอินที่ดูเหมือนว่าจะเพิ่มการรองรับ IE ที่นี่แต่ยังไม่ได้ลองเพียงแค่พบในการค้นหาของ Google
adeneo

2
ในตอนแรกฉันใช้คำตอบของคุณ แต่ความจริงที่ว่ามันไม่ทำงานข้ามเบราว์เซอร์เป็นปัญหา จากนั้นฉันก็หาวิธีแก้ไขอย่างรวดเร็วเพื่อหลีกเลี่ยงปัญหานี้ตั้งแต่แรก หลังจากนั้นไม่กี่สัปดาห์ปัญหาการตรวจจับการแทรกโหนดก็กลับมาอีกครั้งในโค้ดของฉัน คราวนี้รหัสที่ฉันโพสต์ในคำตอบของฉันใช้งานได้จริงอย่างที่ควรจะเป็นข้ามเบราว์เซอร์และไม่มีข้อบกพร่อง นั่นคือเหตุผลที่ฉันย้ายคำตอบว่ายอมรับ นั่นคือวิธีการทำงานของ SO OP จะตัดสินใจว่าคำตอบใดที่เขายอมรับและฝูงชนลงคะแนนให้กับคำตอบ เมื่อเวลาผ่านไปผู้คนจะลงคะแนนและตัดสินใจว่าคำตอบใดเหมาะสมกับพวกเขาที่สุด
เฟรนลี่

2
และฉันอยากจะบอกว่าคุณช่วยฉันหลายครั้งด้วยคำตอบคุณภาพสูงที่ช่วยชีวิตฉันได้ การเปลี่ยนคำตอบที่เป็นที่ยอมรับของฉันนั้นขึ้นอยู่กับรหัสและวิธีที่เหมาะกับปัญหาที่ฉันต้องแก้ไข
เฟรนลี่

ได้รับ "Uncaught TypeError: ไม่สามารถอ่านคุณสมบัติ" มี "ของไม่ได้กำหนด" ด้วยรหัสนี้
gordie

53

นี่คือปลั๊กอินของฉันที่ทำให้แย่ลงอย่างนั้น - jquery.initialize

Useage เหมือนกับที่คุณใช้.eachฟังก์ชัน แต่ด้วย.initializeฟังก์ชันกับองค์ประกอบความแตกต่าง.eachคือจะเริ่มต้นองค์ประกอบที่เพิ่มเข้ามาในอนาคตโดยไม่ต้องใช้รหัสเพิ่มเติมไม่ว่าคุณจะเพิ่มด้วย AJAX หรืออย่างอื่นก็ตาม

Initialize มีรูปแบบเดียวกันกับฟังก์ชัน

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

แต่ตอนนี้ถ้าองค์ประกอบใหม่ที่ตรงกันตัวเลือกองค์ประกอบบางอย่างจะปรากฏบนหน้ามันจะเริ่มต้นทันที วิธีการเพิ่มรายการใหม่ไม่สำคัญคุณไม่จำเป็นต้องกังวลเกี่ยวกับการเรียกกลับใด ๆ ฯลฯ

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

ปลั๊กอินขึ้นอยู่กับ MutationObserver


ขอบคุณมาก.
Brandon Harris

วิธีหยุดการเริ่มต้น ฉันต้องการหยุดดูเมื่อมีการเพิ่มสิ่งที่ฉันกำลังมองหา ...
Evgeny Vostok

$ (". some-element"). initialize [.. ] เลิกใช้งานแล้ว ใช้ $ .initialize (". addDialog", function () {$ ('. noData'). hide ();}); แทน.
Heimi

มันเป็นไปได้ที่จะดำเนินการ.initializeฟังก์ชันcallback เท่านั้นสำหรับองค์ประกอบที่มีการเพิ่มแบบไดนามิก (เช่นผ่านทางอาแจ็กซ์), และข้ามเรียกกลับสำหรับองค์ประกอบอยู่แล้วเมื่อหน้าแรกโหลด? ฉันกำลังพยายามเพิ่มพารามิเตอร์optionsเพื่อที่จะทำสิ่งนี้ให้สำเร็จ แต่จนถึงตอนนี้ฉันยังไม่สามารถใช้งานได้
Nevermore

คุณสามารถเพิ่มปัญหาเกี่ยวกับ github ได้ สำหรับตอนนี้ในทางเทคนิคคุณสามารถทำได้$('.myClass').addClass('ignore-initialize'); $.initialize('.myClass:not(.ignore-initialize)', callback);
Adam Pietrasiak

19

หลังจากตรวจสอบนี้และโพสต์อื่น ๆ อีกหลายฉันพยายามที่จะสกัดสิ่งที่ผมคิดว่าเป็นสิ่งที่ดีที่สุดของแต่ละเป็นสิ่งง่ายๆที่อนุญาตให้ฉันไปตรวจสอบเมื่อระดับขององค์ประกอบที่ถูกแทรกแล้วทำหน้าที่เกี่ยวกับองค์ประกอบเหล่านั้น

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

สิ่งสำคัญสำหรับฉันสิ่งนี้ช่วยให้ a) องค์ประกอบเป้าหมายมีอยู่ที่ระดับความลึกใด ๆ ภายใน "addedNodes" และ b) ความสามารถในการจัดการกับองค์ประกอบเมื่อแทรกครั้งแรกเท่านั้น (ไม่จำเป็นต้องค้นหาการตั้งค่าเอกสารทั้งหมดหรือละเว้น "ประมวลผลแล้ว" ธง)


1
เงื่อนไขของคุณควรเป็นif(mutation.addedNodes.length)เพราะaddedNodesและremovedNodesอาร์เรย์อยู่ในmutationประเภทเสมอchildList(ตรวจสอบใน IE11, Chrome53, FF49) ยังคง +1 เนื่องจากคุณเป็นคำตอบเดียวที่addedNodesจะพิจารณา ( callbackเรียกเฉพาะเมื่อมีการเพิ่มโหนด) ไม่เพียง แต่ในเธรดนี้เท่านั้น แต่ยังมีเธรดอื่น ๆ อีกมากมาย คนอื่นเพียงแค่เรียกใช้callbackสำหรับการกลายพันธุ์ทั้งหมดแม้ว่าโหนดจะถูกลบก็ตาม
Vivek Athalye

17

ประสบการณ์ 3 ปีต่อมานี่คือวิธีที่ฉันฟัง"องค์ประกอบของคลาสที่เพิ่มเข้ามาใน DOM" : คุณเพียงแค่เพิ่ม hook เข้าไปในhtml()ฟังก์ชันjQuery ดังนี้:

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

ใช้งานได้ถ้าคุณใช้ jQuery ซึ่งฉันทำ และไม่ขึ้นอยู่กับเหตุการณ์เฉพาะเบราว์เซอร์DOMNodeInsertedซึ่งไม่สามารถใช้งานข้ามเบราว์เซอร์ได้ ฉันยังเพิ่มการใช้งานเดียวกันสำหรับ.prepend()

โดยรวมแล้วสิ่งนี้เป็นเสน่ห์สำหรับฉันและหวังว่าจะเป็นเช่นนั้นสำหรับคุณด้วย


6
อืม - ฉันขอชี้แจงว่าสิ่งนี้ใช้ได้ผลหากคุณใช้ตัวhtmlช่วยของ jQuery เพื่อแก้ไข DOM เท่านั้นไม่ใช่แค่ "ใช้ jQuery" โดยทั่วไป องค์ประกอบที่เพิ่มโดยใช้พูดว่าjQuery.appendจะไม่เรียกเหตุการณ์นี้
iameli

1
ใช่และนั่นคือเหตุผลที่ฉันชี้แจงว่าฉันเพิ่มการใช้งานเดียวกันกับ .reprend () และถ้าคุณใช้. append () ให้ทำสิ่งเดียวกันกับ. append ()
frenchie

มีวิธีการที่รวดเร็วยิ่งขึ้นโดยใช้ภาพเคลื่อนไหว CSS3 ไลบรารีโอเพนซอร์สยังให้รหัสที่ง่ายกว่ามาก:insertionQ('#intercom-container').every(function(/element){ element.style.display = 'none'; });
Dan Dascalescu

3
ทำไมต้องโหวตลง ?? สิ่งนี้ใช้งานได้จริง: ไม่มีปลั๊กอินและไม่มีฟังก์ชัน setTimeout ที่แฮ็ก
frenchie

2
โหวตแล้ว - นี่คือตัวเลือกที่ถูกต้อง - ทำไมในโลก (!!!) ถึงได้รับการโหวตลง ?????? Dan, CSS3 Animations ไม่ใช่ XBC: caniuse.com/#search=keyframes ; davidwalsh.name/detect-node-insertion
Cody

6

คุณสามารถใช้เหตุการณ์การกลายพันธุ์

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

แก้ไข

จาก MDN: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events

เลิกใช้งานคุณลักษณะนี้ถูกลบออกจากเว็บแล้ว แม้ว่าบางเบราว์เซอร์อาจยังรองรับอยู่ แต่ก็อยู่ในระหว่างการลดลง อย่าใช้ในโครงการเก่าหรือใหม่ เพจหรือเว็บแอพที่ใช้อาจพังได้ตลอดเวลา

Mutation Observers คือการนำเสนอแทนเหตุการณ์การกลายพันธุ์ใน DOM4 จะรวมอยู่ใน Firefox 14 และ Chrome 18

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

MutationObserverช่วยให้นักพัฒนาสามารถตอบสนองต่อการเปลี่ยนแปลงใน DOM ได้รับการออกแบบมาเพื่อทดแทนเหตุการณ์การกลายพันธุ์ที่กำหนดไว้ในข้อกำหนด DOM3 Events

ตัวอย่างการใช้งาน

ตัวอย่างต่อไปนี้ถูกนำมาจากhttp://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-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
เหตุการณ์การกลายพันธุ์ค่อนข้างถูกเลิกใช้ตามmdnเพื่อสนับสนุน MutationObservers
Olga

ตอนนี้ IE10 เป็น EOL ก็น่าจะปลอดภัยแล้วที่จะบอกว่าMutationObserverได้รับการสนับสนุนอย่างกว้างขวาง
rymo

0

คำตอบของ adeneo สองอย่างเพิ่มเติม:

(1) เราสามารถเปลี่ยนสายได้

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

ถึง

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

ดังนั้นเราจะไม่เห็น"Uncaught TypeError: Cannot read property 'contains' of undefined"ข้อผิดพลาด

(2) เพิ่มsubtree: trueในconfigการหาโหนดเพิ่มซ้ำในองค์ประกอบที่เพิ่มเข้ามาตลอด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.