तत्व मौजूद होने तक फ़ंक्शन प्रतीक्षा करें


158

मैं एक कैनवास को दूसरे कैनवास पर जोड़ने की कोशिश कर रहा हूं - जब तक पहला कैनवास नहीं बनता तब तक मैं इस फ़ंक्शन को कैसे शुरू कर सकता हूं?

function PaintObject(brush) {

    this.started = false;

    // get handle of the main canvas, as a DOM object, not as a jQuery Object. Context is unfortunately not yet
    // available in jquery canvas wrapper object.
    var mainCanvas = $("#" + brush).get(0);

    // Check if everything is ok
    if (!mainCanvas) {alert("canvas undefined, does not seem to be supported by your browser");}
    if (!mainCanvas.getContext) {alert('Error: canvas.getContext() undefined !');}

    // Get the context for drawing in the canvas
    var mainContext = mainCanvas.getContext('2d');
    if (!mainContext) {alert("could not get the context for the main canvas");}

    this.getMainCanvas = function () {
        return mainCanvas;
    }
    this.getMainContext = function () {
        return mainContext;
    }

    // Prepare a second canvas on top of the previous one, kind of second "layer" that we will use
    // in order to draw elastic objects like a line, a rectangle or an ellipse we adjust using the mouse
    // and that follows mouse movements
    var frontCanvas = document.createElement('canvas');
    frontCanvas.id = 'canvasFront';
    // Add the temporary canvas as a second child of the mainCanvas parent.
    mainCanvas.parentNode.appendChild(frontCanvas);

    if (!frontCanvas) {
        alert("frontCanvas null");
    }
    if (!frontCanvas.getContext) {
        alert('Error: no frontCanvas.getContext!');
    }
    var frontContext = frontCanvas.getContext('2d');
    if (!frontContext) {
        alert("no TempContext null");
    }

    this.getFrontCanvas = function () {
        return frontCanvas;
    }
    this.getFrontContext = function () {
        return frontContext;
    }

4
जब आप क्लिक पर कैनवास बनाते हैं, तो फ़ंक्शन को चलाएं या एक ऐसी घटना को ट्रिगर करें जो एक हैंडलर चलाता है जो फ़ंक्शन चलाता है। कोई अंतर्निहित क्रॉस-ब्राउज़र घटना नहीं है जो तब होती है जब कोई तत्व उपलब्ध हो जाता है।
केविन बी

जवाबों:


303

यदि आपके पास कैनवास बनाने वाले कोड तक पहुंच है - तो कैनवास बनाने के बाद फ़ंक्शन को वहीं कॉल करें।

यदि आपके पास उस कोड तक पहुंच नहीं है (जैसे। यदि यह एक 3 पार्टी कोड है जैसे कि गूगल मैप्स) तो आप जो कर सकते हैं वह एक अंतराल में अस्तित्व के लिए परीक्षण है:

var checkExist = setInterval(function() {
   if ($('#the-canvas').length) {
      console.log("Exists!");
      clearInterval(checkExist);
   }
}, 100); // check every 100ms

लेकिन ध्यान दें - कई बार 3 पार्टी कोड में आपके कोड को सक्रिय करने का एक विकल्प होता है (कॉलबैक या इवेंट ट्रिगर द्वारा) जब यह लोड करना समाप्त हो जाता है। यह वह जगह हो सकती है जहां आप अपना कार्य कर सकते हैं। अंतराल समाधान वास्तव में एक बुरा समाधान है और इसका उपयोग केवल तभी किया जाना चाहिए जब कुछ और काम न करे।


कोणीयजेस टाइपियाहेड में उपयोग के लिए सही समाधान। मुझे सही दिशा में मार्गदर्शन करने के लिए धन्यवाद!
जुआनत्रेव

1
किसी अन्य चीज को वहां रखने से पहले अजाक्स द्वारा बनाई गई किसी चीज की प्रतीक्षा करने का उत्कृष्ट समाधान। बहुत बहुत धन्यवाद।
काउंट्ज़ेरो

अगर मैं चयनकर्ता एक चर है तो मुझे यह कैसे काम करना है? इसके अलावा, अगर यह एक आईडी या वर्ग चयनकर्ता के रूप में अच्छी तरह से बदलता है। कभी-कभी जब मैं एक वर्ग के साथ चयन करता हूं तो कई तत्व वापस आ जाते हैं, और मुझे यह पता लगाने के लिए चयनकर्ता को एक सूचकांक पास करने का तरीका खोजने की आवश्यकता होगी। यह मैं कैसे करूंगा? धन्यवाद
क्रैग्लोन

@ क्रैग्लोन यह एक पूरी तरह से अलग प्रश्न है और इस उत्तर की टिप्पणियों के लिए उपयुक्त नहीं है। मेरा सुझाव है कि आप एक नया प्रश्न पूछें, समझाएँ कि आपने क्या कोशिश की, क्या समस्या है, आदि ...
इफतह

8
दिए गए समाधान का उपयोग करते समय एक और बात का उल्लेख करना महत्वपूर्ण है, आपके पास लूप के लिए कोड का एक टुकड़ा होना चाहिए और अधिकतम रिट्री काउंटर सेट करना चाहिए, अगर कुछ गलत हो जाता है तो आप अनंत लूप के साथ समाप्त नहीं होते हैं :)
BJ

48

आपको किस ब्राउज़र का समर्थन करना है इसके आधार पर, MutationObserver का विकल्प है ।

संपादित करें: सभी प्रमुख ब्राउज़र अब MutationObserver का समर्थन करते हैं

इस की तर्ज पर कुछ करना चाहिए:

// callback executed when canvas was found
function handleCanvas(canvas) { ... }

// set up the mutation observer
var observer = new MutationObserver(function (mutations, me) {
  // `mutations` is an array of mutations that occurred
  // `me` is the MutationObserver instance
  var canvas = document.getElementById('my-canvas');
  if (canvas) {
    handleCanvas(canvas);
    me.disconnect(); // stop observing
    return;
  }
});

// start observing
observer.observe(document, {
  childList: true,
  subtree: true
});

NB मैंने स्वयं इस कोड का परीक्षण नहीं किया है, लेकिन यह सामान्य विचार है।

आप आसानी से इसे केवल DOM के उस भाग को खोजने के लिए बढ़ा सकते हैं जो बदल गया है। उसके लिए, mutationsतर्क का उपयोग करें , यह MutationRecordवस्तुओं की एक सरणी है ।


2
यह एक प्यार करता था। धन्यवाद।
इस्तीफा दें

1
यह पैटर्न वास्तव में बहुत सारे उदाहरणों में सहायक है, खासकर यदि आप जेएस को एक पृष्ठ में खींच रहे हैं और यह नहीं जानते कि क्या अन्य आइटम लोड किए गए हैं।
नाम के लिए

1
सबसे अच्छा जवाब! धन्यवाद!
एंटनी हैचकिंस

1
मैं एक पुराने ब्राउज़र (ff38) के साथ फंस गया हूं और इसने मुझे बचाया।
जंग भाई

1
ये अद्भुत है! काश, मुझे पता होता कि यह पहले से मौजूद था।
रोब

39

यह केवल आधुनिक ब्राउज़रों के साथ काम करेगा लेकिन मुझे यह आसान लगता है thenइसलिए पहले परीक्षण करें लेकिन:

कोड

function rafAsync() {
    return new Promise(resolve => {
        requestAnimationFrame(resolve); //faster than set time out
    });
}

function checkElement(selector) {
    if (document.querySelector(selector) === null) {
        return rafAsync().then(() => checkElement(selector));
    } else {
        return Promise.resolve(true);
    }
}

या जनरेटर कार्यों का उपयोग करना

async function checkElement(selector) {
    const querySelector = document.querySelector(selector);
    while (querySelector === null) {
        await rafAsync()
    }
    return querySelector;
}  

प्रयोग

checkElement('body') //use whichever selector you want
.then((element) => {
     console.info(element);
     //Do whatever you want now the element is there
});

यहाँ एक त्रुटि है। जनरेटर कार्यों का उपयोग कर, querySelector हर पाश में अद्यतन किया जाना चाहिए:while (document.querySelector(selector) === null) {await rafAsync()}
haofly

31

तत्वों की प्रतीक्षा करने के लिए एक अधिक आधुनिक दृष्टिकोण:

while(!document.querySelector(".my-selector")) {
  await new Promise(r => setTimeout(r, 500));
}
// now the element is loaded

ध्यान दें कि इस कोड को एक async फ़ंक्शन में लपेटना होगा


4
यह बहुत साफ है!
डेक्सटर बेंगिल

rवहाँ क्या है ?
डेनियल मोलर

ठीक है, ठीक है, लेकिन यह कहां से आता है? यह क्या करता है? किसको भेज रहे हो setTimeout?
डेनियल मोलर

@ DanielMöller आप पर एक नज़र लेने के लिए आवश्यकता हो सकती है वादे इस कोड का एक बेहतर समझ पाने के लिए। मूल रूप से यहां जो कोड होता है वह 500ms का टाइमआउट सेट कर रहा है और इसे लूप के एक नए पुनरावृत्ति को किक करने से पहले पूरा होने की प्रतीक्षा करें। चतुर समाधान!
क्लेमेंटपेरिसिआंग

8

यहाँ जेमी हटर के उत्तर पर एक मामूली सुधार किया गया है

const checkElement = async selector => {

while ( document.querySelector(selector) === null) {
    await new Promise( resolve =>  requestAnimationFrame(resolve) )
}

return document.querySelector(selector); };

8

requestAnimationFrameए की तुलना में रिले में बेहतर है setTimeout। यह es6 मॉड्यूल और उपयोग में मेरा समाधान है Promises

es6, मॉड्यूल और वादे:

// onElementReady.js
const onElementReady = $element => (
  new Promise((resolve) => {
    const waitForElement = () => {
      if ($element) {
        resolve($element);
      } else {
        window.requestAnimationFrame(waitForElement);
      }
    };
    waitForElement();
  })
);

export default onElementReady;

// in your app
import onElementReady from './onElementReady';

const $someElement = document.querySelector('.some-className');
onElementReady($someElement)
  .then(() => {
    // your element is ready
  }

plain js and promises:

var onElementReady = function($element) {
  return new Promise((resolve) => {
    var waitForElement = function() {
      if ($element) {
        resolve($element);
      } else {
        window.requestAnimationFrame(waitForElement);
      }
    };
    waitForElement();
  })
};

var $someElement = document.querySelector('.some-className');
onElementReady($someElement)
  .then(() => {
    // your element is ready
  });

Uncaught TypeError: Cannot read property 'then' of undefined
जेफ पकेट

मुझे लगता है कि मैं नया वादा करने से पहले ... वापस लौट आया।
एनक्यूबिका

1
यह उचित समाधान है, जो सभी आवधिक टाइमर-आधारित जाँचों से बहुत बेहतर है।
एंड्रस स्ज़ेपेशज़ी

4
दरअसल, यह अपने मौजूदा स्वरूप में काम नहीं करता है। यदि $ someElement प्रारंभ में शून्य है (अर्थात अभी तक DOM में मौजूद नहीं है), तो आप अपने onElementReady फ़ंक्शन के लिए इस null मान (CSS चयनकर्ता के बजाय) को पास कर देते हैं और तत्व कभी हल नहीं होगा। इसके बजाय, सीएसएस चयनकर्ता को पाठ के रूप में पास करें और प्रत्येक पास पर .querySelector के माध्यम से तत्व का संदर्भ प्राप्त करने का प्रयास करें।
एंड्रस ज़ेपेशज़ी

1
यह मेरे उपयोग के मामले के लिए बहुत अच्छा काम करता है और मेरे लिए एक उचित समाधान लगता है। धन्यवाद
rdhaese

5

यहां वेधशालाओं का उपयोग करके एक समाधान है।

waitForElementToAppear(elementId) {                                          

    return Observable.create(function(observer) {                            
            var el_ref;                                                      
            var f = () => {                                                  
                el_ref = document.getElementById(elementId);                 
                if (el_ref) {                                                
                    observer.next(el_ref);                                   
                    observer.complete();                                     
                    return;                                                  
                }                                                            
                window.requestAnimationFrame(f);                             
            };                                                               
            f();                                                             
        });                                                                  
}                                                                            

अब आप लिख सकते हैं

waitForElementToAppear(elementId).subscribe(el_ref => doSomethingWith(el_ref);

4

आप जाँच कर सकते हैं कि डोम पहले से ही एक टाइमआउट सेट करके मौजूद है जब तक कि यह डोम में पहले से ही रेंडर न हो जाए।

var panelMainWrapper = document.getElementById('panelMainWrapper');
setTimeout(function waitPanelMainWrapper() {
    if (document.body.contains(panelMainWrapper)) {
        $("#panelMainWrapper").html(data).fadeIn("fast");
    } else {
        setTimeout(waitPanelMainWrapper, 10);
    }
}, 10);

3

यदि आप MutationObserver का उपयोग करके एक जेनेरिक समाधान चाहते हैं, तो आप इस फ़ंक्शन का उपयोग कर सकते हैं

// MIT Licensed
// Author: jwilson8767

/**
 * Waits for an element satisfying selector to exist, then resolves promise with the element.
 * Useful for resolving race conditions.
 *
 * @param selector
 * @returns {Promise}
 */
export function elementReady(selector) {
  return new Promise((resolve, reject) => {
    const el = document.querySelector(selector);
    if (el) {resolve(el);}
    new MutationObserver((mutationRecords, observer) => {
      // Query for elements matching the specified selector
      Array.from(document.querySelectorAll(selector)).forEach((element) => {
        resolve(element);
        //Once we have resolved we don't need the observer anymore.
        observer.disconnect();
      });
    })
      .observe(document.documentElement, {
        childList: true,
        subtree: true
      });
  });
}

स्रोत: https://gist.github.com/jwilson8767/db379026efcbd932f64382db4b02853e
उदाहरण इसका उपयोग कैसे करें

elementReady('#someWidget').then((someWidget)=>{someWidget.remove();});

नोट: MutationObserver में एक महान ब्राउज़र समर्थन है; https://caniuse.com/#feat=mutationobserver

Et voilà! :)


2

इफता की एक और भिन्नता

var counter = 10;
var checkExist = setInterval(function() {
  console.log(counter);
  counter--
  if ($('#the-canvas').length || counter === 0) {
    console.log("by bye!");
    clearInterval(checkExist);
  }
}, 200);

बस मामले में तत्व कभी नहीं दिखाया जाता है, इसलिए हम असीम रूप से जांच नहीं करते हैं।

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