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