Javascript:在创建/附加到DOM

时间:2016-08-17 13:51:05

标签: javascript dom chromium-embedded mutation-observers

为了在每个网站上维护正确且高度响应的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代码获取的信息。

2 个答案:

答案 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:

  • 添加了一个子树(在addedNodes中为根节点排队记录)
  • 删除子树的子树(排队记录)
  • 然后附加在第一个子树之外的其他地方(排队另一个记录,哦,男孩。)
  • 删除了从其他子树中删除的元素(将记录排队)
  • 并添加回原始子树(排队记录)

最后,您会收到一份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 ...

回复我发现的内容