Javascript 当一个元素被添加到页面时,我如何得到通知?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7434685/
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
How can I be notified when an element is added to the page?
提问by Kevin Burke
I want a function of my choosing to run when a DOM element is added to the page. This is in the context of a browser extension, so the webpage runs independently of me and I cannot modify its source. What are my options here?
我希望在将 DOM 元素添加到页面时运行我选择的功能。这是在浏览器扩展的上下文中,因此网页独立于我运行,我无法修改其源。我在这里有哪些选择?
I guess that, in theory, I could just use setInterval()
to continually search for the element's presence and perform my action if the element is there, but I need a better approach.
我想,理论上,我可以setInterval()
用来不断搜索元素的存在并在元素存在时执行我的操作,但我需要一个更好的方法。
采纳答案by Jose Faeti
Warning!
This answer is now outdated. DOM Level 4 introduced MutationObserver, providing an effective replacement for the deprecated mutation events. See this answerfor a better solution than the one presented here. Seriously. Don't poll the DOM every 100 milliseconds; it will waste CPU power and your users will hate you.
警告!
这个答案现在已经过时了。DOM Level 4 引入了MutationObserver,为已弃用的突变事件提供了有效的替代。请参阅此答案以获得比此处提供的更好的解决方案。严重地。不要每 100 毫秒轮询一次 DOM;它会浪费 CPU 能力,您的用户会讨厌您。
Since mutation eventswere deprecated in 2012, and you have no control over the inserted elements because they are added by someone else's code, your only option is to continuously check for them.
由于突变事件在 2012 年已被弃用,并且您无法控制插入的元素,因为它们是由其他人的代码添加的,因此您唯一的选择是不断检查它们。
function checkDOMChange()
{
// check for any new element being inserted here,
// or a particular node being modified
// call the function again after 100 milliseconds
setTimeout( checkDOMChange, 100 );
}
Once this function is called, it will run every 100 milliseconds, which is 1/10 (one tenth) of a second. Unless you need real-time element observation, it should be enough.
一旦调用此函数,它将每 100 毫秒运行一次,即 1/10(十分之一)秒。除非你需要实时元素观察,否则应该足够了。
回答by Asfand Qazi
The actual answer is "use mutation observers" (as outlined in this question: Determining if a HTML element has been added to the DOM dynamically), however support (specifically on IE) is limited (http://caniuse.com/mutationobserver).
实际答案是“使用突变观察者”(如本问题所述:确定 HTML 元素是否已动态添加到 DOM 中),但支持(特别是在 IE 上)是有限的(http://caniuse.com/mutationobserver) .
So the actual ACTUAL answer is "Use mutation observers.... eventually. But go with Jose Faeti's answer for now" :)
所以实际的 ACTUAL 答案是“使用突变观察者......最终。但现在使用 Jose Faeti 的答案”:)
回答by jokeyrhyme
Between the deprecation of mutation eventsand the emergence of MutationObserver
, an efficent way to be notified when a specific element was added to the DOM was to exploit CSS3 animation events.
在突变事件的弃用和MutationObserver
DOM的出现之间,当特定元素被添加到 DOM 时,一种有效的通知方式是利用 CSS3 动画事件。
To quote the blog post:
引用博文:
Setup a CSS keyframe sequence that targets (via your choice of CSS selector) whatever DOM elements you want to receive a DOM node insertion event for.
I used a relatively benign and little used css property, clipI used outline-color in an attempt to avoid messing with intended page styles – the code once targeted the clip property, but it is no longer animatable in IE as of version 11. That said, any property that can be animated will work, choose whichever one you like.Next I added a document-wide animationstart listener that I use as a delegate to process the node insertions. The animation event has a property called animationName on it that tells you which keyframe sequence kicked off the animation. Just make sure the animationName property is the same as the keyframe sequence name you added for node insertions and you're good to go.
设置一个 CSS 关键帧序列,以(通过您选择的 CSS 选择器)您想要接收 DOM 节点插入事件的任何 DOM 元素。
我使用了一个相对温和且很少使用的 css 属性 clip我使用了outline-color 是为了避免混淆预期的页面样式——代码曾经针对 clip 属性,但它在 IE 11 版本中不再是可动画的。说,任何可以动画的属性都可以使用,选择你喜欢的任何一个。接下来,我添加了一个文档范围的 animationstart 侦听器,用作处理节点插入的委托。动画事件有一个名为 animationName 的属性,它告诉您哪个关键帧序列启动了动画。只需确保 animationName 属性与您为节点插入添加的关键帧序列名称相同,您就可以开始了。
回答by Zero Distraction
You can use livequery
plugin for jQuery. You can provide a selector expression such as:
您可以使用livequery
jQuery 插件。您可以提供一个选择器表达式,例如:
$("input[type=button].removeItemButton").livequery(function () {
$("#statusBar").text('You may now remove items.');
});
Every time a button of a removeItemButton
class is added a message appears in a status bar.
每次removeItemButton
添加一个类的按钮时,都会在状态栏中显示一条消息。
In terms of efficiency you might want avoid this, but in any case you could leverage the plugin instead of creating your own event handlers.
就效率而言,您可能希望避免这种情况,但在任何情况下,您都可以利用插件而不是创建自己的事件处理程序。
Revisited answer
重温答案
The answer above was only meant to detectthat an item has been added to the DOMthrough the plugin.
上面的答案只是为了检测一个项目已通过插件添加到 DOM 中。
However, most likely, a jQuery.on()
approach would be more appropriate, for example:
但是,最有可能的jQuery.on()
方法是更合适的,例如:
$("#myParentContainer").on('click', '.removeItemButton', function(){
alert($(this).text() + ' has been removed');
});
If you have dynamic content that should respond to clicks for example, it's best to bind events to a parent container using jQuery.on
.
例如,如果您有应该响应点击的动态内容,最好使用jQuery.on
.
回答by Brandon
ETA 24 Apr 17I wanted to simplify this a bit with some async
/await
magic, as it makes it a lot more succinct:
ETA 24 Apr 17我想用一些async
/await
魔法来简化一下,因为它使它更简洁:
Using the same promisified-observable:
使用相同的promisified-observable:
const startObservable = (domNode) => {
var targetNode = domNode;
var observerConfig = {
attributes: true,
childList: true,
characterData: true
};
return new Promise((resolve) => {
var observer = new MutationObserver(function (mutations) {
// For the sake of...observation...let's output the mutation to console to see how this all works
mutations.forEach(function (mutation) {
console.log(mutation.type);
});
resolve(mutations)
});
observer.observe(targetNode, observerConfig);
})
}
Your calling function can be as simple as:
您的调用函数可以很简单:
const waitForMutation = async () => {
const button = document.querySelector('.some-button')
if (button !== null) button.click()
try {
const results = await startObservable(someDomNode)
return results
} catch (err) {
console.error(err)
}
}
If you wanted to add a timeout, you could use a simple Promise.race
pattern as demonstrated here:
如果你想添加一个超时,你可以使用一个简单的Promise.race
模式如下证明:
const waitForMutation = async (timeout = 5000 /*in ms*/) => {
const button = document.querySelector('.some-button')
if (button !== null) button.click()
try {
const results = await Promise.race([
startObservable(someDomNode),
// this will throw after the timeout, skipping
// the return & going to the catch block
new Promise((resolve, reject) => setTimeout(
reject,
timeout,
new Error('timed out waiting for mutation')
)
])
return results
} catch (err) {
console.error(err)
}
}
Original
原来的
You can do this without libraries, but you'd have to use some ES6 stuff, so be cognizant of compatibility issues (i.e., if your audience is mostly Amish, luddite or, worse, IE8 users)
你可以在没有库的情况下做到这一点,但你必须使用一些 ES6 的东西,所以要意识到兼容性问题(即,如果你的观众主要是阿米什人、卢德派或更糟的是 IE8 用户)
First, we'll use the MutationObserver APIto construct an observer object. We'll wrap this object in a promise, and resolve()
when the callback is fired (h/t davidwalshblog)david walsh blog article on mutations:
首先,我们将使用MutationObserver API来构造观察者对象。我们将这个对象包装在一个 promise 中,resolve()
当回调被触发时 (h/t davidwalshblog) david walsh 博客文章关于突变:
const startObservable = (domNode) => {
var targetNode = domNode;
var observerConfig = {
attributes: true,
childList: true,
characterData: true
};
return new Promise((resolve) => {
var observer = new MutationObserver(function (mutations) {
// For the sake of...observation...let's output the mutation to console to see how this all works
mutations.forEach(function (mutation) {
console.log(mutation.type);
});
resolve(mutations)
});
observer.observe(targetNode, observerConfig);
})
}
Then, we'll create a generator function
. If you haven't used these yet, then you're missing out--but a brief synopsis is: it runs like a sync function, and when it finds a yield <Promise>
expression, it waits in a non-blocking fashion for the promise to be fulfilled (Generators do more than this, but this is what we're interested in here).
然后,我们将创建一个generator function
. 如果你还没有使用过这些,那么你就错过了——但一个简短的概要是:它像一个同步函数一样运行,当它找到一个yield <Promise>
表达式时,它以非阻塞方式等待承诺实现了(生成器做的远不止这些,但这是我们在这里感兴趣的)。
// we'll declare our DOM node here, too
let targ = document.querySelector('#domNodeToWatch')
function* getMutation() {
console.log("Starting")
var mutations = yield startObservable(targ)
console.log("done")
}
A tricky part about generators is they don't 'return' like a normal function. So, we'll use a helper function to be able to use the generator like a regular function. (again, h/t to dwb)
关于生成器的一个棘手部分是它们不像普通函数那样“返回”。因此,我们将使用辅助函数来像使用常规函数一样使用生成器。(再次,h/t 到 dwb)
function runGenerator(g) {
var it = g(), ret;
// asynchronously iterate over generator
(function iterate(val){
ret = it.next( val );
if (!ret.done) {
// poor man's "is it a promise?" test
if ("then" in ret.value) {
// wait on the promise
ret.value.then( iterate );
}
// immediate value: just send right back in
else {
// avoid synchronous recursion
setTimeout( function(){
iterate( ret.value );
}, 0 );
}
}
})();
}
Then, at any point before the expected DOM mutation might happen, simply run runGenerator(getMutation)
.
然后,在预期的 DOM 突变可能发生之前的任何时候,只需运行runGenerator(getMutation)
.
Now you can integrate DOM mutations into a synchronous-style control flow. How bout that.
现在您可以将 DOM 更改集成到同步样式的控制流中。那怎么办。
回答by Dave Kiss
There's a promising javascript library called Arrive that looks like a great way to start taking advantage of the mutation observers once the browser support becomes commonplace.
有一个名为 Arrive 的很有前途的 javascript 库,一旦浏览器支持变得司空见惯,它看起来就像是开始利用变异观察器的好方法。
回答by pie6k
Check out this plugin that does exacly that - jquery.initialize
查看这个插件,它确实做到了 - jquery.initialize
It works exacly like .each function, the difference is it takes selector you've entered and watch for new items added in future matching this selector and initialize them
它的工作原理与 .each 函数完全一样,不同之处在于它采用您输入的选择器并观察将来添加的新项目匹配此选择器并初始化它们
Initialize looks like this
初始化看起来像这样
$(".some-element").initialize( function(){
$(this).css("color", "blue");
});
But now if new element matching .some-element
selector will appear on page, it will be instanty initialized.
但是现在如果新的元素匹配.some-element
选择器出现在页面上,它将被立即初始化。
The way new item is added is not important, you dont need to care about any callbacks etc.
添加新项目的方式并不重要,您不需要关心任何回调等。
So if you'd add new element like:
因此,如果您要添加新元素,例如:
$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!
it will be instantly initialized.
它将立即初始化。
Plugin is based on MutationObserver
插件基于 MutationObserver
回答by vedant
A pure javascript solution (without jQuery
):
纯 javascript 解决方案(没有jQuery
):
const SEARCH_DELAY = 100; // in ms
// it may run indefinitely. TODO: make it cancellable, using Promise's `reject`
function waitForElementToBeAdded(cssSelector) {
return new Promise((resolve) => {
const interval = setInterval(() => {
if (element = document.querySelector(cssSelector)) {
clearInterval(interval);
resolve(element);
}
}, SEARCH_DELAY);
});
}
console.log(await waitForElementToBeAdded('#main'));