我正在Chrome中开发一个扩展,我想知道:当一个元素出现时,最好的方法是什么?使用纯javascript,间隔检查,直到一个元素存在,或jQuery有一些简单的方法来做到这一点?
当前回答
如果你想让它在一段时间后停止查看(超时),那么下面的jQuery可以工作。10秒后会暂停。我需要使用这段代码而不是纯JS,因为我需要通过名称选择输入,并且在实现其他一些解决方案时遇到了麻烦。
// Wait for element to exist.
function imageLoaded(el, cb,time) {
if ($(el).length) {
// Element is now loaded.
cb($(el));
var imageInput = $('input[name=product\\[image_location\\]]');
console.log(imageInput);
} else if(time < 10000) {
// Repeat every 500ms.
setTimeout(function() {
time = time+500;
imageLoaded(el, cb, time)
}, 500);
}
};
var time = 500;
imageLoaded('input[name=product\\[image_location\\]]', function(el) {
//do stuff here
},time);
其他回答
一个返回承诺的解决方案,并允许使用超时(兼容IE 11+)。
对于单个元素(element类型):
"use strict";
function waitUntilElementLoaded(selector) {
var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var start = performance.now();
var now = 0;
return new Promise(function (resolve, reject) {
var interval = setInterval(function () {
var element = document.querySelector(selector);
if (element instanceof Element) {
clearInterval(interval);
resolve();
}
now = performance.now();
if (now - start >= timeout) {
reject("Could not find the element " + selector + " within " + timeout + " ms");
}
}, 100);
});
}
对于多个元素(类型为NodeList):
"use strict";
function waitUntilElementsLoaded(selector) {
var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var start = performance.now();
var now = 0;
return new Promise(function (resolve, reject) {
var interval = setInterval(function () {
var elements = document.querySelectorAll(selector);
if (elements instanceof NodeList) {
clearInterval(interval);
resolve(elements);
}
now = performance.now();
if (now - start >= timeout) {
reject("Could not find elements " + selector + " within " + timeout + " ms");
}
}, 100);
});
}
例子:
waitUntilElementLoaded('#message', 800).then(function(element) {
// element found and available
element.innerHTML = '...';
}).catch(function() {
// element not found within 800 milliseconds
});
waitUntilElementsLoaded('.message', 10000).then(function(elements) {
for(const element of elements) {
// ....
}
}).catch(function(error) {
// elements not found withing 10 seconds
});
既适用于元素列表,也适用于单个元素。
更新
下面是一个更新的版本,可以使用承诺。如果达到特定的尝试次数,它也会“停止”。
function _waitForElement(selector, delay = 50, tries = 100) {
const element = document.querySelector(selector);
if (!window[`__${selector}`]) {
window[`__${selector}`] = 0;
window[`__${selector}__delay`] = delay;
window[`__${selector}__tries`] = tries;
}
function _search() {
return new Promise((resolve) => {
window[`__${selector}`]++;
setTimeout(resolve, window[`__${selector}__delay`]);
});
}
if (element === null) {
if (window[`__${selector}`] >= window[`__${selector}__tries`]) {
window[`__${selector}`] = 0;
return Promise.resolve(null);
}
return _search().then(() => _waitForElement(selector));
} else {
return Promise.resolve(element);
}
}
用法很简单,用await使用它只是确保你在一个 异步功能:
const start = (async () => {
const $el = await _waitForElement(`.my-selector`);
console.log($el);
})();
过时的版本
只需添加所需的选择器。一旦找到元素,就可以在回调函数中访问它。
const waitUntilElementExists = (selector, callback) => {
const el = document.querySelector(selector);
if (el){
return callback(el);
}
setTimeout(() => waitUntilElementExists(selector, callback), 500);
}
waitUntilElementExists('.wait-for-me', (el) => console.log(el));
由于性能问题,DOMNodeInserted和其他DOM突变事件已被弃用——推荐的方法是使用MutationObserver监视DOM。不过,它只在较新的浏览器中受支持,所以当MutationObserver不可用时,您应该回到DOMNodeInserted上。
let observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (!mutation.addedNodes) return
for (let i = 0; i < mutation.addedNodes.length; i++) {
// do things to your newly added nodes here
let node = mutation.addedNodes[i]
}
})
})
observer.observe(document.body, {
childList: true
, subtree: true
, attributes: false
, characterData: false
})
// stop watching using:
observer.disconnect()
您可以监听DOMNodeInserted或DOMSubtreeModified事件,每当有新元素添加到DOM时,这些事件就会触发。
还有一个LiveQuery jQuery插件,它可以检测创建的新元素:
$("#future_element").livequery(function(){
//element created
});
这里有一个用Javascript编写的promise返回解决方案(没有混乱的回调)。默认情况下,它每200ms检查一次。
function waitFor(selector) {
return new Promise(function (res, rej) {
waitForElementToDisplay(selector, 200);
function waitForElementToDisplay(selector, time) {
if (document.querySelector(selector) != null) {
res(document.querySelector(selector));
}
else {
setTimeout(function () {
waitForElementToDisplay(selector, time);
}, time);
}
}
});
}