web-dev-qa-db-fra.com

Faire que la fonction attende que l'élément existe

J'essaie d'ajouter une toile par-dessus une autre toile. Comment puis-je faire en sorte que cette fonction attende avant de créer la première toile?

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;
    }
91
Steven

Si vous avez accès au code qui crée la toile - appelez simplement la fonction juste après la création de la toile.

Si vous n'avez pas accès à ce code (par exemple, s'il s'agit d'un code tiers tel que Google Maps), vous pouvez alors tester l'existence dans un intervalle:

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

Mais remarque - le code tiers a souvent la possibilité d'activer votre code (par rappel ou déclenchement d'événement) une fois le chargement terminé. C'est peut-être où vous pouvez mettre votre fonction. La solution d'intervalle est vraiment une mauvaise solution et ne doit être utilisée que si rien d'autre ne fonctionne.

196
Iftah

Selon le navigateur que vous devez prendre en charge, l'option/ MutationObserver est disponible.

Quelque chose du genre devrait faire l'affaire:

// 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. Je n'ai pas testé ce code moi-même, mais c'est l'idée générale.

Vous pouvez facilement l'étendre pour ne rechercher que la partie du DOM modifiée. Pour cela, utilisez l’argument mutations, c’est un tableau de MutationRecord objects.

22
damd

Cela fonctionnera uniquement avec les navigateurs modernes, mais je trouve qu'il est plus facile d'utiliser simplement une then. Veuillez d'abord tester, mais:

Code

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

Ou en utilisant les fonctions du générateur

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

Utilisation

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

Une approche plus moderne de l'attente d'éléments:

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

Notez que ce code devra être encapsulé dans une fonction async .

12
user993683

Mieux vaut relayer dans requestAnimationFrame que dans un setTimeout. C’est ma solution dans les modules es6 et avec Promises.

es6, modules et promesses:

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

Vous pouvez vérifier si le dom existe déjà en définissant un délai d'expiration jusqu'à ce qu'il soit déjà rendu dans le dom.

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

C’est très simple - n’appelez pas cette fonction tant que vous n’avez pas déjà créé le canevas (c’est-à-dire que vous le faites dans ce gestionnaire click).

0
Alnitak

Voici une amélioration mineure par rapport à la réponse de Jamie Hutber

const checkElement = async selector => {

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

return document.querySelector(selector); };
0
wLc