检测/监控DOM变化的最有效方法是什么?

51

我需要一种高效的机制来检测DOM的变化。最好是跨浏览器的,但如果有任何高效的方法跨浏览器,我可以用可靠的跨浏览器方法来实现。

特别是,我需要检测可能影响页面文本的变化,因此必须包括任何新元素、被删除的元素或修改了内部文本(innerHTML)的元素。

我无法控制所做的更改(它们可能是由第三方javascript包括等引起的),因此无法从这个角度进行处理——我需要以某种方式“监视”变化。

目前我已经实施了一个“快速而简单”的方法,定期检查body.innerHTML.length。当然,这不会检测到导致返回相同长度的更改,但在这种情况下是“足够好的”——出现这种情况的可能性非常小,在这个项目中,未能检测到更改不会导致数据丢失。

body.innerHTML.length的问题在于它很耗费资源。在一个快速的浏览器上,它可能需要1到5毫秒,这可能会使事情变得非常缓慢——我还要处理大量的iframe,这会增加一些负担。我相当确定这样做昂贵是因为innerHTML文本不是被浏览器静态存储的,每次读取时都需要从DOM中计算出来。

我正在寻找的答案类型包括从“精确”(例如事件)到“足够好”的任何东西——也许像innerHTML.length方法那样“快速而简单”,但执行速度更快。

编辑:我还应该指出,虽然检测到已修改的精确元素会很“好”,但并不是绝对必要的——只要有任何变化就足够了。希望这能扩大人们的回答范围。我将调查Mutation Events,但我仍然需要一个IE支持的备用方案,因此任何奇怪、创造性、超出传统想法的建议将非常受欢迎。


上次有人问这个问题时并没有太多的运气:https://dev59.com/2XRB5IYBdhLWcg3wXWG2 - Roatin Marth
是的,我看到了,你说得对...运气不太好。然而,因为我不需要100%的准确性,也因为我不需要精确定位哪个元素已经改变,只需要知道有一个变化,所以我希望人们能为我想出一些创造性的解决方案... - Graza
1
可能是JavaScript/jQuery DOM更改监听器?的重复问题。 - Makyen
6个回答

35

10

谢谢,这似乎涵盖了所有基础 - 对于非IE的DOMTree事件和对于IE的onPropertyChange。我还没有实现onPropertyChange事件,但会这样做 - 请查看@Gaby帖子上的我的评论以获取迄今为止已完成的更新。 - Graza
jQuery的live()方法在IE中似乎不太好用,因此我还使用了标准的bind()(然后在发生任何更改时使用unbind().bind(),以防有新内容添加),但是使用DOMTree事件、onPropertyChange和setInterval回退似乎可以解决问题。 watch()似乎太麻烦了(必须手动添加到所有元素,因为它不是标准事件类型,而只是一个方法),而且我不确定各种浏览器是否正确支持它,而其他两种方法似乎已经涵盖了大部分问题。 - Graza
2
FYI: "自 jQuery 1.7 版本起,.live() 方法已被弃用。请使用 .on() 来绑定事件处理程序。对于早期版本的 jQuery 用户,应优先使用 .delegate() 方法代替 .live()。" [来自文档] (http://api.jquery.com/live/#typefn) - Carl G
2
附加信息:mozilla文档指出,观察变异事件已被弃用,应该使用“变异观察器”进行替换。不过我不知道变异观察器是否已经可用。 - Carl G

8

变异事件是您正在寻找的W3建议。

不确定它们是否得到了全面支持.. (IE很可能不支持它们..)


我现在正在使用这些事件 - 我仍然会定期检查 body.innerHTML.length,但如果触发了任何这些事件,我会假设不再需要周期性检查并将其关闭。我仍然要调查 onPropertyChangewatch() 作为替代方案,并且可能会使用类似的想法来实现这些功能,即如果它们被触发,我可以取消任何其他检查 - onPropertyChange 可能适用于 IE,但不适用于其他浏览器。 - Graza
1
变异事件目前已被弃用,改为使用变异观察器。https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver - Adam Grant

1
我最近编写了一个插件,可以完美实现这一点 - jquery.initialize
使用方法与.each函数相同。
$(".some-element").initialize( function(){
    $(this).css("color", "blue"); 
});

.each不同的是,它使用您的选择器,在本例中为.some-element,并等待将来具有此选择器的新元素,如果添加了这样的元素,则也将初始化它。
在我们的示例中,initialize函数只是将元素颜色更改为蓝色。因此,如果我们添加新元素(无论是使用ajax还是甚至F12检查器或其他任何东西),例如:
$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!

插件会立即初始化。此外,插件确保一个元素仅被初始化一次。因此,如果您添加了元素,然后从body中.deatch()它,然后再次添加它,它将不会被再次初始化。
$("<div/>").addClass('some-element').appendTo("body").detach()
    .appendTo(".some-container");
//initialized only once

插件基于MutationObserver - 它将在IE9和10上工作,但需要详细说明的依赖项,请参见自述页

1

它们是DOM 2级中引入的两种变异事件类型之一。还存在少量其他事件类型。 - Gabriele Petrioli

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接