为了在每个网站上维护正确且高度响应的GUI覆盖,我需要尽快注册并分析每个相关的DOM元素。目前我正在使用MutationObserver,它为我工作并简化,它看起来像这样:
var observer = new MutationObserver(
function(mutations){
mutations.forEach(
function(mutation){
if(mutation.type == 'childList')
{
var nodes = mutation.addedNodes;
var n = nodes.length;
for(var i = 0; i < n; i++)
{
if(nodes[i].nodeType == 1) // ELEMENT_NODE
{
// Save it in specific Array or something like this
AnalyzeNode(nodes[i]);
}
}
}
}
);
}
);
var config = {subtree: true, childList: true};
observer.observe(document, config);
但我已经意识到,使用过的MutationObserver不会为DOM中包含的每个节点调用 AnalyzeNode 。当在DOM之外创建已完成的(子)树时(例如,通过在页面加载时执行外部JS脚本)并将其根目录附加到DOM mutation.addedNodes 将仅包含子树&# 39; s的根和它的所有孩子都会被忽视(因为那里不会发生进一步的突变),成为DOM的一部分但没有被分析过。
我想知道附加节点是否已经有 childNodes 来将其标识为附加子树的根,但不幸的是,似乎每个 addedNode 都可能有目前,MutationObserver的函数被调用。所以不可能以这种方式区分。
我真的不想在MutationObserver处理其父节点时仔细检查添加节点(父节点)的每个子节点。大多数情况下,子节点仍将由MutationObserver处理,当其自身将成为另一个发生的突变中 addedNodes 的一部分时,开销似乎变得不必要的高。
此外,我想到了一组节点,其子节点必须在MutationObserver调用之外进行分析。如果添加的节点在其附加到DOM时具有子节点,则该节点将添加到Set中。当发生另一个突变并且其中一个子节点是 addedNodes 的一部分时,其子节点通过使用 mutation.target (它是父节点)从Set中删除其父节点( mutation.type 必须是 childList )。这种方法的问题是何时检查Set中节点的子节点(事实上,我可以查询 document.getElementsByTagname 以获取每个相关的Element类型而不是维护Set,但是计时问题仍然存在)。请记住,应该尽快保持叠加响应和适合网站。 文档 onreadystatechange 的组合以及将新脚本节点附加到DOM(作为执行外部JS代码时的指示符)可能甚至可以用于网站,重新创建其内容的一部分(我正在看你duckduckgo搜索结果页面)。但它似乎是一种解决方法,在100%的情况下都不会解决问题。
那么,还有另一种更有效的方法吗?或者,如果略有改变,这些方法中的任何一种都可能就足够非常感谢!
(请尽量避免使用JQuery作为示例代码,谢谢。顺便说一句,我使用的是CEF,所以最好的情况是使用Webkit / Blink的解决方案)
EDIT1:网站渲染由CEF在内部完成,GUI渲染由C ++ / OpenGL完成,其中包含通过上述Javascript代码获取的信息。
答案 0 :(得分:1)
您的实际目标似乎是在渲染输出中布局检测更改,而不是(可能不可见)DOM更改。
在基于gecko的浏览器上,你可以使用MozAfterPaint
来获知更改区域的边界框,这是相当精确但有一些间隙,例如视频播放(更改显示内容但不更改布局)或异步滚动。
也可以通过CSSOM更改布局,例如通过操纵<style>.sheet.cssRules
。已经在评论中提到的CSS动画是另一个也可以影响没有突变的布局的东西。可能还有SMIL动画。
因此,单独使用变异观察者可能是不够的。
如果您的叠加层具有一些可利用的几何属性,那么另一种可能性可能是通过document.elementFromPoint对您重要的视口部分进行采样并计算找到的元素及其子项的边界框,直到您拥有所需的任何内容为止。通过requestAnimationFrame()
进行调度意味着你应该能够在每一帧上对当前布局的状态进行采样,除非它被其他rAF回调改变,在你的后面运行。
最后,大多数可用的方法似乎都有一些差距,或者需要仔细调整以避免占用过多的CPU时间。
或者,如果略有改变,这些方法中的任何一种都可能是足够的吗?
将观察到的突变和WeakSet
的树行走组合到不处理已访问过的节点可能会有一些更仔细的过滤。
答案 1 :(得分:0)
MutationRecords似乎是listed in the order in which the changes happened(可以轻松验证)。
在运行AnalyzeNode(nodes[i])
算法之前,您可以运行AnalyzeChanges(mutations)
步骤,以确定发生的所有更改。
例如,如果您看到addedNodes
包含相同节点10次,但您在removedNodes
中只看到相同节点9次,那么您知道最终结果是该节点最终添加到DOM。
当然它可能比这更复杂,你必须检测添加的子树,然后可能已经从那些子树中删除和添加的节点等。
最后,一旦你知道净变化是什么,你可以运行AnalyzeNode(nodes[i])
。
我正在考虑这样做以观察整个<svg>
树并在WebGL中渲染它(并在发生更改时重新渲染它)。
这可能很棘手,因为想象下面的一些用户(你不知道他/她将要做什么)同时发生以下操作,他们正在操纵DOM:
最后,您会收到一份MutationRecords列表,其中详细列出了所有这些步骤。
我们可以遍历所有记录并基本上重新创建所发生的事情,然后我们可以找出发生的最终净变化。
在我们进行了这些净更改之后,它就像拥有一个记录列表,但它们会更简单,(例如删除然后添加一个节点只是取消它,所以我们不是真的关心节点是否被删除然后立即添加,因为最终结果基本上没有该节点。)
人们试图解决检测树木之间变化的问题。
这些解决方案中的许多都与术语&#34;虚拟dom&#34;和&#34; dom diffing&#34;就网络而言,Google上有yield results。
所以,而不是做所有的突变分析(这听起来像一场噩梦,但如果你这样做,我会说请请将其作为开源发布,以便人们可以受益),我们可以可能使用diffing工具来查找突变之前的DOM与MutationObserver回调被触发时的DOM之间的差异。
例如,virtual-dom有办法get a diff。我还没有尝试过。我也不确定如何从DOM树创建VTrees以将它们传递到diff()
函数。
啊哈!使用diffDOM获得差异可能更容易。
获取diff可能会提供从一棵树转换到另一棵树所需的最简单的变更集,这可能比分析变异记录列表容易得多。值得一试。我可以用<svg>
s ...