jQuery 使函数等待元素存在
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16149431/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Make function wait until element exists
提问by Steven
I'm trying to add a canvas over another canvas – how can I make this function wait to start until the first canvas is created?
我正在尝试在另一个画布上添加一个画布——我怎样才能让这个函数等到第一个画布创建后才开始?
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;
}
回答by Iftah
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:
如果您无法访问该代码(例如,如果它是 3rd 方代码,例如谷歌地图),那么您可以做的是在一个时间间隔内测试是否存在:
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.
但请注意 - 很多时候 3rd 方代码可以选择在完成加载时激活您的代码(通过回调或事件触发)。那可能是您可以放置函数的地方。间隔解决方案确实是一个糟糕的解决方案,只有在其他方法都不起作用时才应该使用它。
回答by damd
Depending on which browser you need to support, there's the option of MutationObserver.
根据您需要支持的浏览器,有MutationObserver选项。
EDIT: All major browsers support MutationObserver now.
编辑:所有主要浏览器现在都支持 MutationObserver。
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.
您可以轻松地将其扩展为仅搜索更改的 DOM 部分。为此,使用mutations
参数,它是一个MutationRecord
对象数组。
回答by Jamie Hutber
This will only work with modern browsers but I find it easier to just use a then
so please test first but:
这仅适用于现代浏览器,但我发现使用 a 更容易,then
所以请先测试,但是:
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);
}
}
Or using generator functions
或者使用生成器函数
async function checkElement(selector) {
const querySelector = document.querySelector(selector);
while (querySelector === null) {
await rafAsync()
}
return querySelector;
}
Usage
用法
checkElement('body') //use whichever selector you want
.then((element) => {
console.info(element);
//Do whatever you want now the element is there
});
回答by Jamie Hutber
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.
请注意,此代码需要包含在异步函数中。
回答by wLc
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); };
回答by ncubica
Is better to relay in requestAnimationFrame
than in a setTimeout
. this is my solution in es6 modules and using Promises
.
中继requestAnimationFrame
比在setTimeout
. 这是我在 es6 模块中的解决方案并使用Promises
.
es6, modules and 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
:
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
});
回答by FrankL
Here is a solution using observables.
这是一个使用 observables 的解决方案。
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();
});
}
Now you can write
现在你可以写
waitForElementToAppear(elementId).subscribe(el_ref => doSomethingWith(el_ref);
回答by Carmela
You can check if the dom already exists by setting a timeout until it is already rendered in the dom.
您可以通过设置超时来检查 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);
回答by heriberto perez
Another variation of Iftah
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);
Just in case the element is never shown, so we don't check infinitely.
以防万一元素从未显示,所以我们不会无限地检查。
回答by rdhainaut
If you want a generic solution using MutationObserver you can use this function
如果您想要使用 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
});
});
}
Source: https://gist.github.com/jwilson8767/db379026efcbd932f64382db4b02853e
Example how to use it
来源:https: //gist.github.com/jwilson8767/db379026efcbd932f64382db4b02853e
使用示例
elementReady('#someWidget').then((someWidget)=>{someWidget.remove();});
Note: MutationObserver has a great browser support; https://caniuse.com/#feat=mutationobserver
注意:MutationObserver 有很好的浏览器支持;https://caniuse.com/#feat=mutationobserver
Et voilà ! :)
等等!:)