Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make function wait until element exists

People also ask

How to wait until an element exists JavaScript?

To wait to do something until an element exists in the DOM, you can use JavaScript MutatationObserver API. As the name suggests, it can be used to listen out for changes in the DOM. You can then fire a (callback) function in response to this.


If you have access to the code that creates the canvas - simply call the function right there after the canvas is created.

If you have no access to that code (eg. If it is a 3rd party code such as google maps) then what you could do is test for the existence in an interval:

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

But note - many times 3rd party code has an option to activate your code (by callback or event triggering) when it finishes to load. That may be where you can put your function. The interval solution is really a bad solution and should be used only if nothing else works.


Depending on which browser you need to support, there's the option of MutationObserver.

EDIT: All major browsers support MutationObserver now.

Something along the lines of this should do the trick:

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

N.B. I haven't tested this code myself, but that's the general idea.

You can easily extend this to only search the part of the DOM that changed. For that, use the mutations argument, it's an array of MutationRecord objects.


This will only work with modern browsers but I find it easier to just use a then so please test first but:

ES5

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

ES6

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

Usage

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

A more modern approach to waiting for elements:

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

Note that this code would need to be wrapped in an async function.


Here's a minor improvement over Jamie Hutber's answer

const checkElement = async selector => {
  while ( document.querySelector(selector) === null) {
    await new Promise( resolve =>  requestAnimationFrame(resolve) )
  }
  return document.querySelector(selector); 
};

To use:

checkElement('.myElement').then((selector) => {
  console.log(selector);
});

If you want a generic solution using MutationObserver you can use this function

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

Source: https://gist.github.com/jwilson8767/db379026efcbd932f64382db4b02853e
Example how to use it

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

Note: MutationObserver has a great browser support; https://caniuse.com/#feat=mutationobserver

Et voilà ! :)