我正在尝试在另一个画布上添加画布 - 如何让这个函数等到第一个画布创建之后才开始?
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;
}
答案 0 :(得分:208)
如果您可以访问创建画布的代码 - 只需在创建画布后立即调用该函数。
如果您无法访问该代码(例如,如果它是第三方代码,例如谷歌地图),那么您可以做的是测试间隔中是否存在:
var checkExist = setInterval(function() {
if ($('#the-canvas').length) {
console.log("Exists!");
clearInterval(checkExist);
}
}, 100); // check every 100ms
但请注意 - 很多时候第三方代码可以选择在加载完成后激活您的代码(通过回调或事件触发)。这可能是你可以放置你的功能的地方。区间解决方案实际上是一个糟糕的解决方案,只有在没有其他工作的情况下才能使用。
答案 1 :(得分:24)
根据您需要支持的浏览器,可以选择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
});
N.B。我自己没有测试过这段代码,但这是一般的想法。
您可以轻松地将其扩展为仅搜索已更改的DOM部分。为此,使用mutations
参数,它是MutationRecord
个对象的数组。
答案 2 :(得分:22)
这只适用于现代浏览器,但我发现使用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) {
while (document.querySelector(selector) === null) {
await rafAsync()
}
return true;
}
<强>用法强>
checkElement('body') //use whichever selector you want
.then((element) => {
console.info(element);
//Do whatever you want now the element is there
});
答案 3 :(得分:17)
等待元素的更现代的方法:
while(!document.querySelector(".my-selector")) {
await new Promise(r => setTimeout(r, 500));
}
// now the element is loaded
请注意,此代码需要包含在async function。
中答案 4 :(得分:9)
如果您想要使用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
等等! :)
答案 5 :(得分:4)
最好在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
});
答案 6 :(得分:3)
您可以通过设置超时来检查dom是否已经存在,直到它已经在dom中呈现。
var panelMainWrapper = document.getElementById('panelMainWrapper');
setTimeout(function waitPanelMainWrapper() {
if (document.body.contains(panelMainWrapper)) {
$("#panelMainWrapper").html(data).fadeIn("fast");
} else {
setTimeout(waitPanelMainWrapper, 10);
}
}, 10);
答案 7 :(得分:3)
与Jamie Hutber的回答相比,这里有一个小改进
const checkElement = async selector => {
while ( document.querySelector(selector) === null) {
await new Promise( resolve => requestAnimationFrame(resolve) )
}
return document.querySelector(selector); };
答案 8 :(得分:3)
这是使用可观察对象的解决方案。
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);
答案 9 :(得分:3)
var counter = 10;
var checkExist = setInterval(function() {
console.log(counter);
counter--
if ($('#the-canvas').length || counter === 0) {
console.log("by bye!");
clearInterval(checkExist);
}
}, 200);
以防万一该元素从不显示,因此我们不会进行无限检查。
答案 10 :(得分:1)
也许我有点晚了:),但这里是 chrisjhoughton 的一个不错的简短解决方案,它允许在等待结束时执行回调函数。
https://gist.github.com/chrisjhoughton/7890303
var waitForEl = function(selector, callback) {
if (jQuery(selector).length) {
callback();
} else {
setTimeout(function() {
waitForEl(selector, callback);
}, 100);
}
};
waitForEl(selector, function() {
// work the magic
});
如果需要给回调函数传递参数,可以这样使用:
waitForEl("#" + elDomId, () => callbackFunction(param1, param2));
但是要小心!默认情况下,此解决方案可能会陷入无限循环的陷阱。
The GitHub thread 中还提供了对 topicstarter 建议的一些改进。
享受吧!
答案 11 :(得分:0)
只需在递归中使用setTimeOut
:
waitUntilElementIsPresent(callback: () => void): void {
if (!this.methodToCheckIfElementIsPresent()) {
setTimeout(() => this.waitUntilElementIsPresent(callback), 500);
return;
}
callback();
}
用法:
this.waitUntilElementIsPresent(() => console.log('Element is present!'));
您可以限制尝试次数,因此,在限制之后不存在该元素时,将引发错误:
waitUntilElementIsPresent(callback: () => void, attempt: number = 0): void {
const maxAttempts = 10;
if (!this.methodToCheckIfElementIsPresent()) {
attempt++;
setTimeout(() => this.waitUntilElementIsPresent(callback, attempt), 500);
return;
} else if (attempt >= maxAttempts) {
return;
}
callback();
}