使函数等待元素存在

我试图在另一个画布上添加一个画布-我如何使这个函数等待开始,直到第一个画布被创建?

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;
}
319065 次浏览

如果你有权限访问创建画布的代码,只需在画布创建后调用函数即可。

如果您无法访问该代码(例如。如果它是第三方代码,如谷歌maps),那么你可以做的是在一个区间内测试是否存在:

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

但注意-很多时候第三方代码有一个选项来激活你的代码(通过回调或事件触发)当它完成加载。你可以把函数放在这里。区间解实际上是一个糟糕的解,只有在其他方法都不起作用的情况下才应该使用。

您可以通过设置超时来检查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);

根据你需要支持的浏览器,有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
});

注意:我自己还没有测试过这段代码,但这是大概的想法。

您可以轻松地将其扩展为只搜索DOM中发生更改的部分。为此,使用mutations参数,它是MutationRecord对象的数组。

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

这将只适用于现代浏览器,但我发现它更容易使用then,所以请先测试,但是:

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

使用

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

一种更现代的等待元素的方法:

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

注意,这段代码需要包装在异步函数中。

这是对杰米·哈伯的回答的一个小小的改进

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

使用方法:

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

这里有一个使用可观测数据的解决方案。

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

Iftah的另一个变体

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

以防元素从未显示,所以我们不进行无限检查。

如果你想要一个通用的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
});
});
}
< p >来源:https://gist.github.com/jwilson8767/db379026efcbd932f64382db4b02853e < br > 示例如何使用

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

注意:MutationObserver有很好的浏览器支持;https://caniuse.com/#feat=mutationobserver

Et voilà !:)

只需使用带有递归的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();
}

也许我有点晚了:),但这里有一个漂亮而简短的解决方案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));

但是要小心!默认情况下,这个解决方案会陷入一个无限循环的陷阱。

在GitHub线程中还提供了对topicstarter建议的几个改进。

享受吧!

这是为那些你是在Chrome控制台运行代码,而不是硬编码到html。

上面User993683提供的代码将在您的控制台代码中工作。他/她的代码如下:

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

他/她补充说,它&;需要在异步函数中."如果你在Chrome的控制台中使用代码,那么实际上你需要将它包装在一个函数中。它会像写的那样工作。您只需要将它放在代码中试图访问元素之前的位置,以确保它存在。

唯一需要注意的是,它不适用于在其他情况下偶尔出现的元素。否则,如果元素从未下载,它将无限循环,您将不得不关闭浏览器来停止等待。只对您确定会出现的元素使用它。

我的公司的表单页面为每个案例号都有十几个或更多的字段需要填写。我每天在脚本数组中有数百个案例编号。当改变iFrame SRC和“onload”时,元素不会同时加载。不工作在Chrome控制台脚本。所以这个方法对我来说是天赐之物,它每天为我节省了至少45分钟,而不是因为加载时间的波动而在这里等10秒或在那里等30秒。

我所做的唯一更改是“getelementbyid”;而不是一般的“queryselector”;因为我需要的所有元素都有ID。

while(!document.getElementById("myFrame").contentWindow.document.getElementById('someDocID')) {
await new Promise(r => setTimeout(r, 500));
}
// After completing the wait above it is now safe to access the element
document.getElementById("myFrame").contentWindow.document.getElementById('someDocID'
).innerText = "Smith, John R";
// and now click the submit button then change the SRC to a fresh form, and use
//*emphasized text* the code again to wait for it to fully load

我向监控器道歉,但我添加了这个作为回答,因为在对控制台脚本进行了几个月的研究并等待元素加载之后,user993683关于函数的评论最终使我意识到控制台脚本不需要此代码的函数。我在这里的目标只是让其他consoler脚本用户不必像我一样经历同样的学习曲线。

这是一种纯粹的基于承诺的JavaScript方法,您可以知道需要等待多少毫秒。

    const waitElementFor = function(query, ms = 3000) { // 3000 === 3 seconds
return new Promise((resolve) => {
var waited = 0;
var el = null;
var wi = setInterval(function() {
el = document.querySelector(query);
if (waited >= ms || el) {
clearInterval(wi);
if(el) {
resolve(el);
} else {
resolve(null);
}
}
waited += 10;
}, 10);
});
}

要使用该函数,只需在异步函数中使用以下代码。

var element = await waitElementFor('#elementID');

代码片段:

const waitElementFor = function(query, ms = 3000) { // 3000 === 3 seconds
return new Promise((resolve) => {
var waited = 0;
var el = null;
var wi = setInterval(function() {
el = document.querySelector(query);
if (waited >= ms || el) {
clearInterval(wi);
if(el) {
resolve(el);
} else {
resolve(null);
}
}
waited += 10;
}, 10);
});
}


async function snippetTestAyncFunction(){
var element = await waitElementFor('#elementID');
console.log(element);
}


snippetTestAyncFunction();