当一个元素被添加到页面中时,如何通知我?
How can I be notified when an element is added to the page?
我想让我选择的函数在DOM元素添加到页面时运行。这是在浏览器扩展的背景下,所以网页独立于我运行,我不能修改它的源代码。我有什么选择?
我想,从理论上讲,我可以使用setInterval()
不断地搜索元素的存在,并在元素存在时执行操作,但我需要一种更好的方法。
警告!
这个答案已经过时了。DOM Level 4引入了MutationObserver,为已弃用的突变事件提供了有效的替代。请参阅对另一个问题的回答,以获得比这里提供的解决方案更好的解决方案。认真对待。不要每100毫秒轮询一次DOM;它会浪费CPU的能量,你的用户会讨厌你。
由于突变事件在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 );
}
一旦这个函数被调用,它将每100毫秒运行一次,也就是1/10(十分之一)秒。除非您需要实时的元素观察,否则它应该足够了。
实际的答案是"使用突变观察者"(如本问题所述:确定HTML元素是否已动态添加到DOM中),但是支持(特别是在IE上)是有限的(http://caniuse.com/mutationobserver)。
所以实际的答案是"使用突变观察者...."最终。但是请接受Jose Faeti现在的回答:)
在弃用突变事件和MutationObserver
出现之间,当一个特定元素被添加到DOM中时,一个有效的通知方法是利用CSS3动画事件。
引用博客文章:
设置一个CSS关键帧序列,目标(通过你选择的CSS选择器)任何你想接收一个DOM节点插入事件的DOM元素。
我使用了一个相对温和且很少使用的css属性,clip我使用了outline-color,以避免混淆预期的页面样式-代码曾经针对clip属性,但它在IE版本11中不再是可动画的。也就是说,任何可以动画的属性都可以工作,选择你喜欢的。接下来,我添加了一个文档范围的animationstart侦听器,我将其用作处理节点插入的委托。动画事件有一个名为animationName的属性,它告诉你哪个关键帧序列启动了动画。只要确保animationName属性与你为节点插入添加的关键帧序列名称相同,就可以了。
埃塔24日4月17日我想用一些async
/await
魔法来简化这一点,因为它使它更简洁:
使用相同的承诺可观察对象:
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);
})
}
你的调用函数可以像
一样简单: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)
}
}
如果您想添加超时,您可以使用一个简单的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)
}
}
原来你可以在没有库的情况下做到这一点,但你必须使用一些ES6的东西,所以要认识到兼容性问题(例如,如果你的受众主要是Amish, luddite或更糟的是IE8用户)
首先,我们将使用MutationObserver API来构造一个观察者对象。当回调被触发时,我们将把这个对象包装在一个promise和resolve()
中(h/t davidwalshblog)。
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);
})
}
然后,我们将创建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")
}
关于生成器的一个棘手的部分是它们不像普通函数那样"返回"。因此,我们将使用辅助函数来像使用常规函数一样使用生成器。(同样,h/t to 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 );
}
}
})();
}
然后,在预期的DOM突变可能发生之前的任何时刻,只需运行runGenerator(getMutation)
。
现在可以将DOM突变集成到同步风格的控制流中。
你可以使用livequery
插件jQuery。您可以提供一个选择器表达式,如:
$("input[type=button].removeItemButton").livequery(function () {
$("#statusBar").text('You may now remove items.');
});
每次添加removeItemButton
类的按钮时,状态栏都会显示一条消息。
考虑到效率,你可能希望避免这种情况,但在任何情况下,你都可以利用插件而不是创建自己的事件处理程序。
重新回答
上面的答案只是用来检测一个条目已经通过插件添加到DOM。
然而,最有可能的是,jQuery.on()
方法更合适,例如:
$("#myParentContainer").on('click', '.removeItemButton', function(){
alert($(this).text() + ' has been removed');
});
如果你有动态的内容,应该响应点击,例如,最好使用jQuery.on
将事件绑定到父容器
看看这个插件,它就是这样做的- jquery.initialize
它的工作原理与。each函数完全相同,不同之处在于它接受你输入的选择器并观察将来添加的与此选择器匹配的新项并初始化它们
初始化如下所示
$(".some-element").initialize( function(){
$(this).css("color", "blue");
});
但是现在如果新的元素匹配.some-element
选择器将出现在页面上,它将被立即初始化。
添加新项的方式并不重要,你不需要关心任何回调等。
所以如果你添加新的元素,比如:
$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!
将立即初始化。
插件是基于MutationObserver
一个纯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'));
使用jQuery你可以做-
function nodeInserted(elementQuerySelector){
if ($(elementQuerySelector).length===0){
setTimeout(function(){
nodeInserted(elementQuerySelector);
},100);
}else{
$(document).trigger("nodeInserted",[elementQuerySelector]);
}
}
函数递归地搜索节点,直到找到它,然后针对文档
触发一个事件那么你可以用这个来实现它
nodeInserted("main");
$(document).on("nodeInserted",function(e,q){
if (q === "main"){
$("main").css("padding-left",0);
}
});
- Firebase2(Firebase.google.com)推送通知-从外部管理
- 无法在窗口内使用rootScope.通知
- JS,用于播放提示音以通知未按预期工作
- 单击Chrome通知后,转到已打开的选项卡
- Windows Azure通知中心错误
- Meteor应用程序上的警报/通知不会出现
- GCM推送通知,如果应用程序在手机中关闭(Phonegap Android)
- 在通知点击时使用Ion中的OneSignal插件路由到应用程序中的状态
- PHP/JS-EchoJs函数PHP curl发布后的通知
- 将属性设置为未定义时未通知观察者
- Acync JS HTTP请求通知请求
- 在渲染骨干视图时获得通知
- 是否可以使用推送通知Windows 8 Metro应用程序's使用Javascript
- 如何设置特定时间的本地通知
- 如何使用Quickblox在Cordova应用程序中实现推送通知支持
- 通过$.when传播进度通知
- 在Strongloop中发送推送通知时,设备从安装中删除
- 如何安全地获取&使用Facebook应用程序访问令牌发送通知使用PHP&Javascript
- Gulp复制任务完成后如何执行通知
- 网站通知