जब कोई तत्व पृष्ठ में जोड़ा जाता है तो मुझे कैसे सूचित किया जा सकता है?


139

जब कोई DOM तत्व पेज में जोड़ा जाता है तो मैं उसे चलाने के लिए अपने चयन का कार्य चाहता हूं। यह एक ब्राउज़र एक्सटेंशन के संदर्भ में है, इसलिए वेबपेज मेरे द्वारा स्वतंत्र रूप से चलता है और मैं इसके स्रोत को संशोधित नहीं कर सकता। मेरे लिए यहां क्या विकल्प हैं?

मुझे लगता है कि, सिद्धांत रूप में, मैं बस setInterval()तत्व की उपस्थिति के लिए लगातार खोज करने और तत्व होने पर अपनी कार्रवाई करने के लिए उपयोग कर सकता हूं , लेकिन मुझे बेहतर दृष्टिकोण की आवश्यकता है।


क्या आपको किसी विशेष तत्व के लिए जांच करनी है कि आपकी एक अन्य स्क्रिप्ट पृष्ठ में डाली गई है या किसी ऐसे तत्व के लिए जिसे स्रोत से कोई फर्क नहीं पड़ता है?
जोस फेटी

1
क्या आप जानते हैं कि फ़ंक्शन किसी और के कोड में तत्व को जोड़ता है, यदि ऐसा है तो आप इसे अधिलेखित कर सकते हैं और एक कस्टम घटना को ट्रिगर करने वाली एक अतिरिक्त रेखा जोड़ सकते हैं?
जेफरीदेव

जवाबों:


86

चेतावनी!

यह उत्तर अब पुराना हो चुका है। DOM लेवल 4 ने उत्परिवर्तन उत्परिवर्तन घटनाओं के लिए एक प्रभावी प्रतिस्थापन प्रदान करते हुए MutationObserver को पेश किया । यहां प्रस्तुत एक से बेहतर समाधान के लिए इस उत्तर को देखें । गंभीरता से। DOM को हर 100 मिलीसेकंड पर पोल न करें; यह CPU शक्ति को बर्बाद करेगा और आपके उपयोगकर्ता आपसे नफरत करेंगे।

चूंकि उत्परिवर्तन की घटनाओं को 2012 में हटा दिया गया था, और आपके पास सम्मिलित तत्वों पर कोई नियंत्रण नहीं है क्योंकि उन्हें किसी और के कोड द्वारा जोड़ा जाता है, आपका एकमात्र विकल्प उनके लिए लगातार जांच करना है।

function checkDOMChange()
{
    // check for any new element being inserted here,
    // or a particular node being modified

    // call the function again after 100 milliseconds
    setTimeout( checkDOMChange, 100 );
}

एक बार इस फ़ंक्शन को कॉल करने पर, यह प्रत्येक 100 मिलीसेकंड पर चलेगा, जो कि एक सेकंड का 1/10 (दसवां) है। जब तक आपको वास्तविक समय तत्व अवलोकन की आवश्यकता नहीं होती है, तब तक यह पर्याप्त होना चाहिए।


1
जब आप वास्तव में शर्त पूरी करते हैं, तो आप अपना सेटलमेंट कैसे खत्म करते हैं? यानी, आपको वह तत्व मिल गया जो आखिरकार आपके पेज पर x सेकंड बाद लोड हुआ?
क्लेविस

1
@blachawk आपको एक चर के लिए सेटटाइमआउट रिटर्न मान निर्दिष्ट करने की आवश्यकता है, जिसे आप बाद में इसे खाली करने के लिए क्लियर टाइमआउट () के लिए पैरामीटर के रूप में पास कर सकते हैं।
जोस फेटी

3
@blackhawk: मैंने अभी उस उत्तर को देखा और इसे +1 किया; लेकिन आपको पता होगा कि आपको वास्तव में क्लियरटाइम () का उपयोग करने की आवश्यकता नहीं है, क्योंकि सेटटाइमआउट () केवल एक बार चलता है! एक 'अगर-और' बस पर्याप्त है: एक बार जब आप सम्मिलित तत्व को पा लेते हैं, तो सेटटाइमआउट () पर निष्पादन को पास न होने दें।
1111161171159459134

उपयोग करने के लिए एक उपयोगी व्यावहारिक मार्गदर्शिका MutationObserver(): davidwalsh.name/mutationobserver-api
gfullam

4
वह गंदा है। क्या वास्तव में AS3 के लिए "समकक्ष" नहीं है Event.ADDED_TO_STAGE?
बिटरब्ले जुले

45

वास्तविक उत्तर "म्यूटेशन ऑब्जर्वर का उपयोग करें" (जैसा कि इस प्रश्न में उल्लिखित है: यह निर्धारित करना कि क्या HTML तत्व को DOM में जोड़ा गया है ), हालाँकि समर्थन (विशेष रूप से IE पर) सीमित है ( http://caniuse.com/mutationobserver ) ।

तो वास्तविक वास्तविक उत्तर है "उत्परिवर्तन पर्यवेक्षकों का उपयोग करें .... अंततः। लेकिन अभी के लिए जोस फेटी के उत्तर के साथ जाएं" :)


19

बीच की निंदा उत्परिवर्तन घटनाओं और के उद्भव MutationObserverजब एक विशिष्ट तत्व डोम के लिए किया गया था जोड़ा गया है, एक कुशल तरीका अधिसूचित किया जाना CSS3 के एनीमेशन घटनाओं का फायदा उठाने

ब्लॉग पोस्ट को उद्धृत करने के लिए:

एक सीएसएस कीफ़्रेम अनुक्रम को सेट करें जो आपके सीएसएस चयनकर्ता की (आपकी पसंद के माध्यम से) जो भी डोम तत्व आप के लिए एक डोम नोड प्रविष्टि घटना प्राप्त करना चाहते हैं। मैं अपेक्षाकृत सौम्य और कम इस्तेमाल की गई सीएसएस संपत्ति का उपयोग करता था, क्लिप मैंने इच्छित पृष्ठ शैलियों के साथ खिलवाड़ से बचने के प्रयास में आउटलाइन-रंग का उपयोग किया था - कोड ने एक बार क्लिप संपत्ति को लक्षित किया था, लेकिन यह IE में अब संस्करण 11 के रूप में एनिमेटेबल नहीं है। कहा, कोई भी संपत्ति जो एनिमेटेड हो सकती है, काम करेगी, जिसे भी आप पसंद करते हैं उसे चुनें।

इसके बाद मैंने एक डॉक्यूमेंट-वाइड एनिमेशनस्टार्ट श्रोता को जोड़ा, जिसका उपयोग मैं नोड सम्मिलन को संसाधित करने के लिए एक प्रतिनिधि के रूप में करता हूं। एनीमेशन इवेंट में एक संपत्ति होती है जिसे एनीमेशननाम कहा जाता है, जो आपको बताती है कि किस keyframe अनुक्रम ने एनीमेशन को किक किया। बस यह सुनिश्चित कर लें कि एनीमेशननाम गुणक उसी तरह है जैसे कि केफ्रेम अनुक्रम नाम जिसे आपने नोड सम्मिलन के लिए जोड़ा है और आप जाने के लिए अच्छे हैं।


यह उच्चतम-प्रदर्शन समाधान है, और इसके लिए एक लाइब्रेरी का उल्लेख करने वाले डुप्लिकेट प्रश्न का उत्तर है
डैन डैस्केल्स्कु

1
अधूरा जवाब।
गोचन कर्ट

सावधान। यदि मिलीसेकंड सटीक महत्वपूर्ण है, तो यह दृष्टिकोण सटीक से बहुत दूर है। यह jsbin दर्शाता है कि एक इनलाइन कॉलबैक और उपयोग करने के बीच 30ms से अधिक अंतर है animationstart, jsbin.com/netuquralu/1/edit
गजस

मैं कर्ट से सहमत हूं, यह अंडररेटेड है।
Zabbu

13

आप livequeryjQuery के लिए प्लगइन का उपयोग कर सकते हैं । आप एक चयनकर्ता अभिव्यक्ति प्रदान कर सकते हैं जैसे:

$("input[type=button].removeItemButton").livequery(function () {
    $("#statusBar").text('You may now remove items.');
});

जब भी किसी removeItemButtonकक्षा का एक बटन जोड़ा जाता है तो एक संदेश स्थिति पट्टी में दिखाई देता है।

दक्षता के मामले में आप इससे बच सकते हैं, लेकिन किसी भी स्थिति में आप अपने स्वयं के ईवेंट हैंडलर बनाने के बजाय प्लगइन का लाभ उठा सकते हैं।

पुनः उत्तर दिया गया

ऊपर दिए गए जवाब का मतलब केवल यह पता लगाना था कि प्लगइन के माध्यम से DOM में कोई आइटम जोड़ा गया है ।

हालांकि, सबसे अधिक संभावना है, एक jQuery.on()दृष्टिकोण अधिक उपयुक्त होगा, उदाहरण के लिए:

$("#myParentContainer").on('click', '.removeItemButton', function(){
          alert($(this).text() + ' has been removed');
});

यदि आपके पास गतिशील सामग्री है जो उदाहरण के लिए क्लिकों का जवाब देना चाहिए, तो घटनाओं का उपयोग करके एक माता-पिता कंटेनर में बांधना सबसे अच्छा है jQuery.on


11

ईटीए 24 अप्रैल 17 मैं कुछ async/ awaitजादू के साथ इसे थोड़ा सरल करना चाहता था , क्योंकि यह इसे बहुत अधिक सफल बनाता है:

उसी प्रॉमिसिफाइड-ऑब्जर्वेबल का उपयोग करना:

const startObservable = (domNode) => {
  var targetNode = domNode;

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

  return new Promise((resolve) => {
      var observer = new MutationObserver(function (mutations) {
         // For the sake of...observation...let's output the mutation to console to see how this all works
         mutations.forEach(function (mutation) {
             console.log(mutation.type);
         });
         resolve(mutations)
     });
     observer.observe(targetNode, observerConfig);
   })
} 

आपका कॉलिंग फ़ंक्शन उतना ही सरल हो सकता है:

const waitForMutation = async () => {
    const button = document.querySelector('.some-button')
    if (button !== null) button.click()
    try {
      const results = await startObservable(someDomNode)
      return results
    } catch (err) { 
      console.error(err)
    }
}

यदि आप एक टाइमआउट जोड़ना चाहते हैं, तो आप यहां प्रदर्शित एक सरल Promise.raceपैटर्न का उपयोग कर सकते हैं :

const waitForMutation = async (timeout = 5000 /*in ms*/) => {
    const button = document.querySelector('.some-button')
    if (button !== null) button.click()
    try {

      const results = await Promise.race([
          startObservable(someDomNode),
          // this will throw after the timeout, skipping 
          // the return & going to the catch block
          new Promise((resolve, reject) => setTimeout(
             reject, 
             timeout, 
             new Error('timed out waiting for mutation')
          )
       ])
      return results
    } catch (err) { 
      console.error(err)
    }
}

मूल

आप पुस्तकालयों के बिना भी ऐसा कर सकते हैं, लेकिन आपको कुछ ES6 सामानों का उपयोग करना होगा, इसलिए संगतता के मुद्दों से सावधान रहें (यानी, यदि आपके दर्शक ज्यादातर अमीश, लुडाइट या बदतर, IE8 उपयोगकर्ता हैं)

सबसे पहले, हम ऑब्जर्वर ऑब्जेक्ट बनाने के लिए MutationObserver API का उपयोग करेंगे । हम इस ऑब्जेक्ट को एक वादा में resolve()लपेटेंगे , और जब कॉलबैक निकाल दिया जाएगा (h / t davidwalshblog) म्यूटेशन पर walsh ब्लॉग लेख :

const startObservable = (domNode) => {
    var targetNode = domNode;

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

    return new Promise((resolve) => {
        var observer = new MutationObserver(function (mutations) {
            // For the sake of...observation...let's output the mutation to console to see how this all works
            mutations.forEach(function (mutation) {
                console.log(mutation.type);
            });
            resolve(mutations)
        });
        observer.observe(targetNode, observerConfig);
    })
} 

फिर, हम एक बनाएंगे generator function। यदि आपने अभी तक इनका उपयोग नहीं किया है, तो आप गायब हो रहे हैं - लेकिन एक संक्षिप्त सारांश है: यह सिंक फ़ंक्शन की तरह चलता है, और जब यह एक yield <Promise>अभिव्यक्ति पाता है , तो यह वादे के लिए गैर-अवरुद्ध फैशन में इंतजार करता है पूरा ( जेनरेटर इससे अधिक करते हैं, लेकिन यह वही है जो हम यहां रुचि रखते हैं )।

// we'll declare our DOM node here, too
let targ = document.querySelector('#domNodeToWatch')

function* getMutation() {
    console.log("Starting")
    var mutations = yield startObservable(targ)
    console.log("done")
}

जनरेटर के बारे में एक मुश्किल हिस्सा यह है कि वे एक सामान्य कार्य की तरह 'वापसी' नहीं करते हैं। तो, हम एक हेल्पर फंक्शन का उपयोग करेंगे जो एक नियमित फंक्शन की तरह जनरेटर का उपयोग करने में सक्षम होगा। (फिर से, h / t से dwb )

function runGenerator(g) {
    var it = g(), ret;

    // asynchronously iterate over generator
    (function iterate(val){
        ret = it.next( val );

        if (!ret.done) {
            // poor man's "is it a promise?" test
            if ("then" in ret.value) {
                // wait on the promise
                ret.value.then( iterate );
            }
            // immediate value: just send right back in
            else {
                // avoid synchronous recursion
                setTimeout( function(){
                    iterate( ret.value );
                }, 0 );
            }
        }
    })();
}

फिर, अपेक्षित DOM उत्परिवर्तन से पहले किसी भी बिंदु पर, बस चला सकते हैं runGenerator(getMutation)

अब आप DOM म्यूटेशन को सिंक्रोनस-स्टाइल कंट्रोल फ्लो में एकीकृत कर सकते हैं। कैसे डटकर।


8

अर्रव नामक एक आशाजनक जावास्क्रिप्ट लाइब्रेरी है जो ब्राउज़र समर्थन के सामान्य हो जाने के बाद म्यूटेशन ऑब्जर्वर का लाभ लेना शुरू करने का एक शानदार तरीका दिखता है।

https://github.com/uzairfarooq/arrive/


3

इस प्लगइन की जाँच करें जो कि एक्सेली करता है - jquery.initialize

यह .each फ़ंक्शन की तरह काम करता है, अंतर यह है कि चयनकर्ता आपके द्वारा दर्ज किए गए और भविष्य में इस चयनकर्ता से मेल खाते हुए नए आइटमों को देखता है और उन्हें इनिशियलाइज़ करता है।

इनिशियलाइज़ इस तरह दिखता है

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

लेकिन अब यदि नया एलिमेंट मैचिंग .some-elementसिलेक्टर पेज पर दिखाई देगा, तो इसे तुरंत इनिशियल किया जाएगा।

जिस तरह से नया आइटम जोड़ा जाता है वह महत्वपूर्ण नहीं है, आपको किसी कॉलबैक आदि के बारे में परवाह करने की आवश्यकता नहीं है।

इसलिए यदि आप नया तत्व जोड़ेंगे जैसे:

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

इसे तुरंत शुरू कर दिया जाएगा।

प्लगइन पर आधारित है MutationObserver


0

एक शुद्ध जावास्क्रिप्ट समाधान (बिना jQuery):

const SEARCH_DELAY = 100; // in ms

// it may run indefinitely. TODO: make it cancellable, using Promise's `reject`
function waitForElementToBeAdded(cssSelector) {
  return new Promise((resolve) => {
    const interval = setInterval(() => {
      if (element = document.querySelector(cssSelector)) {
        clearInterval(interval);
        resolve(element);
      }
    }, SEARCH_DELAY);
  });
}

console.log(await waitForElementToBeAdded('#main'));
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.