我正在Chrome中开发一个扩展,我想知道:当一个元素出现时,最好的方法是什么?使用纯javascript,间隔检查,直到一个元素存在,或jQuery有一些简单的方法来做到这一点?
当前回答
下面的observe函数将允许您通过选择器监听元素。
在下面的例子中,2秒过后,.greeting将被插入到.container中。因为我们正在监听这个元素的插入,所以我们可以有一个在插入时触发的回调。
const observe = (selector, callback, targetNode = document.body) => new MutationObserver(mutations => [...mutations] .flatMap((mutation) => [...mutation.addedNodes]) .filter((node) => node.matches && node.matches(selector)) .forEach(callback)) .observe(targetNode, { childList: true, subtree: true }); const createGreeting = () => { const el = document.createElement('DIV'); el.textContent = 'Hello World'; el.classList.add('greeting'); return el; }; const container = document.querySelector('.container'); observe('.greeting', el => console.log('I have arrived!', el), container); new Promise(res => setTimeout(() => res(createGreeting()), 2000)) .then(el => container.appendChild(el)); html, body { width: 100%; height: 100%; margin: 0; padding: 0; } body { display: flex; } .container { display: flex; flex: 1; align-items: center; justify-content: center; } .greeting { font-weight: bold; font-size: 2em; } <div class="container"></div>
更新
下面是一个实验性的async/await示例。
const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); const observe = (selector, targetNode = document.body) => new Promise(res => { new MutationObserver(mutations => res([...mutations] .flatMap((mutation) => [...mutation.addedNodes]) .find((node) => node.matches && node.matches(selector)))) .observe(targetNode, { childList: true, subtree: true }); }); const createGreeting = () => { const el = document.createElement('DIV'); el.textContent = 'Hello World'; el.classList.add('greeting'); return el; }; const container = document.querySelector('.container'); observe('.greeting', container) .then(el => console.log('I have arrived!', el)); (async () => { await sleep(2000); container.appendChild(createGreeting()); })(); html, body { width: 100%; height: 100%; margin: 0; padding: 0; } body { display: flex; } .container { display: flex; flex: 1; align-items: center; justify-content: center; } .greeting { font-weight: bold; font-size: 2em; } <div class="container"></div>
其他回答
这里有一个核心JavaScript函数,用于等待元素的显示(好吧,将其插入到DOM中更准确)。
// Call the below function
waitForElementToDisplay("#div1",function(){alert("Hi");},1000,9000);
function waitForElementToDisplay(selector, callback, checkFrequencyInMs, timeoutInMs) {
var startTimeInMs = Date.now();
(function loopSearch() {
if (document.querySelector(selector) != null) {
callback();
return;
}
else {
setTimeout(function () {
if (timeoutInMs && Date.now() - startTimeInMs > timeoutInMs)
return;
loopSearch();
}, checkFrequencyInMs);
}
})();
}
这个调用将每1000毫秒查找id="div1"的HTML标记。如果找到元素,它将显示一条警报消息Hi。如果在9000毫秒后没有找到任何元素,该函数将停止执行。
参数:
String:该函数查找元素${selector}。 callback: Function:这是一个函数,如果找到元素将被调用。 checkFrequencyInMs: Number:该函数每${checkFrequencyInMs}毫秒检查该元素是否存在。 timeoutInMs: Number:可选。该函数在${timeoutInMs}毫秒后停止查找元素。
注意:选择器的解释在https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
如果你想让它在一段时间后停止查看(超时),那么下面的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);
您可以监听DOMNodeInserted或DOMSubtreeModified事件,每当有新元素添加到DOM时,这些事件就会触发。
还有一个LiveQuery jQuery插件,它可以检测创建的新元素:
$("#future_element").livequery(function(){
//element created
});
你也可以使用getElementById代替querySelector
这个函数和 https://stackoverflow.com/a/61511955/10798137
async function waitForElementById(id, timeout = null, location = document.body) {
return new Promise((resolve) => {
let element = document.getElementById(id);
if (element) {
return resolve(element);
}
const observer = new MutationObserver(async () => {
let element = document.getElementById(id);
if (element) {
resolve(element);
observer.disconnect();
} else {
if (timeout) {
async function timeOver() {
return new Promise((resolve) => {
setTimeout(() => {
observer.disconnect();
resolve(false);
}, timeout);
});
}
resolve(await timeOver());
}
}
});
observer.observe(location, {
childList: true,
subtree: true,
});
});
使用它
waitForElement("tag_id", 500).then((elm) => {
console.log(elm)
})
Or
var elm = async waitForElement("tag_id", 500)
我也有同样的问题,所以我继续写了一个插件。
$(选择).waitUntilExists(函数);
代码:
;(function ($, window) {
var intervals = {};
var removeListener = function(selector) {
if (intervals[selector]) {
window.clearInterval(intervals[selector]);
intervals[selector] = null;
}
};
var found = 'waitUntilExists.found';
/**
* @function
* @property {object} jQuery plugin which runs handler function once specified
* element is inserted into the DOM
* @param {function|string} handler
* A function to execute at the time when the element is inserted or
* string "remove" to remove the listener from the given selector
* @param {bool} shouldRunHandlerOnce
* Optional: if true, handler is unbound after its first invocation
* @example jQuery(selector).waitUntilExists(function);
*/
$.fn.waitUntilExists = function(handler, shouldRunHandlerOnce, isChild) {
var selector = this.selector;
var $this = $(selector);
var $elements = $this.not(function() { return $(this).data(found); });
if (handler === 'remove') {
// Hijack and remove interval immediately if the code requests
removeListener(selector);
}
else {
// Run the handler on all found elements and mark as found
$elements.each(handler).data(found, true);
if (shouldRunHandlerOnce && $this.length) {
// Element was found, implying the handler already ran for all
// matched elements
removeListener(selector);
}
else if (!isChild) {
// If this is a recurring search or if the target has not yet been
// found, create an interval to continue searching for the target
intervals[selector] = window.setInterval(function () {
$this.waitUntilExists(handler, shouldRunHandlerOnce, true);
}, 500);
}
}
return $this;
};
}(jQuery, window));