差分进化变异算子
我曾经是DOM Mutation Events的忠实拥护者 。 它们为脚本提供了一种独特的方式来监视DOM中的更改,而与导致它们的事件或操作无关。 因此,诸如DOMNodeInserted
和DOMAttrModified
类的事件将分别响应节点的添加或属性更改而触发。
但是,如果您从未使用过突变事件,那真的就不足为奇了,因为在大多数情况下,是您添加这些节点或更改那些属性的原因,为什么您首先需要对您引起的事件进行React性事件?
因此,它们通常用于解决库和框架中的问题,例如响应匿名闭包引起的更改。 对于许多浏览器扩展来说 ,它们也是一个库存,它们提供了检测文档何时更改的最简单,有时甚至是唯一的方法。
语法非常简单,就像其他事件一样:
element.addEventListener('DOMNodeInserted', function(e)
{
console.log('Added ' + e.target.nodeName
+ ' to ' + element.nodeName);
}, false);
但是,这种简单性掩盖了一个潜在的问题-突变事件的实施不充分,并且使浏览器开发遇到性能和稳定性问题。 它们触发频率太高,它们缓慢且难以优化,并且它们是许多潜在崩溃错误的源头。
这就是为什么突变事件已被弃用大约两年的原因,并且名义上完全不允许Firefox附加组件包含它们。 实际上,当我去年发布Dust-Me Selectors的更新时,我必须获得特别许可才能继续使用它们!
请注意, DOMContentLoaded
不是突变事件,它只是具有类似的名称。 该事件不存在此类问题,因此不建议使用该事件。
你不能放下一个好主意
尽管存在这些问题,但突变事件的想法仍然是一个好主意 ,不久之后Mozilla和Google的开发人员提出了一项新提案 ,该提案很快就被DOM 4规范接受。
新的API称为MutationObserver
,它比突变事件复杂得多,但是这种复杂性大大提高了控制和精度。
这是一个简单的示例,它响应document.body
节点的添加,并向控制台写入每个更改的摘要:
var watcher = new MutationObserver(function(mutations)
{
mutations.forEach(function(mutation)
{
for(var i = 0; i < mutation.addedNodes.length; i ++)
{
console.log('Added ' + mutation.addedNodes[i].nodeName + ' to ' + mutation.target.nodeName);
}
});
});
向观察者回调传递了一个对象,该对象包含有关突变的数据,其每个成员代表一个更改。 这与突变事件不同,后者会为每个更改单独触发回调!
每个突变对象中包含的数据取决于观察到的内容。 在这种情况下,我们仅关注目标元素的子项(由配置对象中的childList
参数指定)的变化,因此,突变对象具有addedNodes
属性,该属性是对每个添加节点的引用的集合。
这是该示例的演示,可在Firefox 14或更高版本以及Chrome 18或更高版本中运行 :
该演示有一个按钮,您可以单击该按钮以将新段落添加到页面,并且每次发生时,观察者都会做出响应。 当然,在实践中,您不会这样做-您只需要使用click
事件来触发它是什么-但关键是观察者可以响应任何事物 (包括(尤其是)您没有的脚本) 引起的更改其他控制。
我敢肯定,您可以开始想象用户脚本和浏览器扩展的潜力,从而能够准确地响应DOM中的任何更改,无论它们是由脚本编写还是由直接的用户交互(例如,当用户输入到contentEditable
区域)。
一些令人惊讶的可能性
现在,如果您查看Firefox中的演示,您会注意到该控制台已经显示了多个变化,甚至在您单击该按钮之前。 发生这种情况是因为观察者本身没有包装在DOMContentLoaded
,所以它在脚本执行后立即开始工作。 我偶然发现了这一点,这仅仅是因为我更愿意在可能的情况下使用这种方式编写脚本,并且我意识到这种突变是浏览器在<body>
添加了节点,即在包含<script>
之后的每个节点都添加了一个节点。
Chrome不会这样做-我只能怀疑它是故意阻止的-因为就我们对DOM脚本的工作方式而言,它非常有意义。 我们知道脚本是同步执行的,这就是为什么可以在完成渲染之前将其添加到<body>
的原因。 因此,如果我们开始观察DOM的变化,即使该变化是由浏览器自己的呈现引起的,我们也应该获得此后发生的每个变化的通知。
这使我想到了几年前的一个想法 ,即一个可以在文档加载和呈现过程中为几个不同点提供回调的库。 我从来没有提出过这个想法,因为它会遭受如此残酷的黑客攻击-但是使用变异观察者将是微不足道的。 我们要做的就是在主体的开头添加观察者,然后我们可以坐下来观察浏览器逐个节点绘制它!
签出(在Firefox 14或更高版本中):
每天都有更多可能性
![](https://img-blog.csdnimg.cn/2022010709395958861.png)
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
但是实际上,大多数变异观察者并不需要那么广泛,事实上,他们的技巧和精确度是他们美丽的一部分。 浏览器不必报告每个微小的变化,只需要我们过滤数据以找到我们想要的内容(这对我们来说是乏味的,对浏览器来说效率很低)。 使用变异观察者,您只需要处理您所关心的事情,并且只要您需要了解的时间就可以。
这是另一个示例,它firstChild
元素的文本(即元素的firstChild
文本节点)的更改,然后在发生更改时立即停止监视:
(new MutationObserver(function(mutations, self)
{
mutations.forEach(function(mutation)
{
console.log('Changed text from "' + mutation.oldValue + '" to "' + mutation.target.nodeValue + '"');
});
self.disconnect();
})).observe(element.firstChild, { characterData : true, characterDataOldValue : true });
请注意,我在那里使用了稍微不同的语法-而不是将实例化保存到变量中,而是将其括在方括号中,因此我们可以将observe()
命令直接链接到末尾。 在观察者内部,对实例本身的引用将传递给回调,然后我们可以使用该引用断开连接。
结论
这是对突变观察者的广泛介绍,相当详细地介绍了如何使用它们。 我什至没有提到Chrome的实现WebKitMutationObserver
前缀(现在可以作为WebKitMutationObserver
)的WebKitMutationObserver
。 但我想主要关注此新API的背景,并开始对各种可能性感到兴奋!
如果有需求,我将写一篇后续文章来详细研究它们,但现在,我建议您访问MDN的MutationObserver
文档 。 Mozilla Hacks博客上还有另一篇好文章。
当我听到突变事件正在消失时,我感到非常震惊,因为还有什么可以做同样的工作? 事实证明,毕竟还有别的东西-而且要好一百倍!
差分进化变异算子