Javascript:在创建/附加到 DOM 时注册*每个* DOM 元素

标签 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 将仅包含子树的根并且它的所有子节点都将被忽视(因为那里不会发生进一步的突变),成为 DOM 的一部分,但尚未被分析。

我的想法是检查附加节点是否可能已经有 childNodes 来将其标识为附加子树的根,但不幸的是,似乎每个 addedNode 都可能有调用 MutationObserver 函数时的子进程。所以这种方式不可能有区别。

我真的不想在 MutationObserver 处理其父节点时仔细检查添加节点(父节点)的每个子节点。大多数时候,当子节点本身成为其他发生的突变中的 addedNodes 的一部分时,子节点仍然会由 MutationObserver 处理,并且开销似乎变得不必要的高。

此外,我考虑了一组节点,必须在 MutationObserver 调用之外分析其子节点。如果添加的节点在附加到 DOM 时有子节点,则该节点将添加到 Set 中。当发生另一次突变并且其子节点之一是addedNodes的一部分时,其子节点使用mutation.target从集合中删除其父节点 - 这是父节点( mutation.type 必须是childList)。这种方法的问题是检查 Set 中节点的子节点的时间(事实上,我可以查询 document.getElementsByTagname 来查找每个相关的 Element 类型,而不是维护一个 Set,但是时间问题仍然存在)。请记住,应该尽快使覆盖层保持响应并适合网站。 documentonreadystatechange 和向 DOM 附加新的 script 节点(作为外部 JS 代码执行时的指示器)的组合甚至可能有效对于网站,重新创建其部分内容(我正在查看您的 duckduckgo 搜索结果页面)。但这似乎是一种解决方法,并不能在 100% 的情况下解决问题。

那么,还有其他更有效的方法吗?或者如果稍微改变一下这些方法是否就足够了?非常感谢!

(请尽量避免使用 JQuery 作为示例代码,谢谢。顺便说一句,我正在使用 CEF,所以最好的情况是与 Webkit/Blink 一起使用的解决方案)

编辑1:网站渲染由CEF内部完成,GUI渲染由C++/OpenGL完成,信息由上述Javascript代码获取。

最佳答案

看来您的实际目标是布局检测渲染输出中的更改,而不是(可能不可见的)DOM 更改。

在基于 Gecko 的浏览器上,您可以使用 MozAfterPaint获取更改区域的边界框的通知,这相当精确,但有一些差距,例如视频播放(更改显示内容但不更改布局)或异步滚动。

布局也可以通过 CSSOM 更改,例如通过操纵 <style>.sheet.cssRules 。 CSS 动画(已经在评论中提到)是另一个可以在不发生突变的情况下影响布局的东西。可能还有 SMIL 动画。

因此,单独使用突变观察器可能是不够的。

如果您的叠加层具有一些可利用的几何属性,那么另一种可能性可能是通过document.elementFromPoint对视口(viewport)中对您很重要的部分进行采样。并计算找到的元素及其子元素的边界框,直到获得所需的内容。通过 requestAnimationFrame() 安排意味着您应该能够在每一帧上对当前布局的状态进行采样,除非它被在您的帧之后运行的其他 rAF 回调更改。

最后,大多数可用的方法似乎都存在一些差距,或者需要仔细调整,以免占用太多 CPU 时间。

Or does any of these approaches may be sufficient if slightly changed?

结合观察到的突变的树行走和 WeakSet不处理已经访问过的节点可能需要一些更仔细的过滤。

  • 已经访问过某个节点并不自动意味着您可以跳过其子节点
  • 但是拜访过一个本身不是突变目标的 child 应该意味着你可以跳过它
  • 删除事件意味着您必须从集合中逐个节点删除整个子树,或者只是清除集合,因为它们可能会移动到树中的另一个点

关于Javascript:在创建/附加到 DOM 时注册*每个* DOM 元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38998599/

相关文章:

delphi - 如何检索 XHR 响应?

c++ - 如何将视频帧从 C++ 传递到 chromium 嵌入式框架(cef)?

c++ - CEF 从嵌入式资源加载 HTML

javascript - 获取 XMLHttpRequest 无法加载(URL) 预检响应无效(重定向)

JavaScript/jQuery : how to catch a click that targets any element except for several specified elements?

javascript - 将 AngularJS Controller 分离到单独的文件中

javascript - 如何获取html标题元素

HTML 内联元素和垂直/水平边距

javascript - 如何制作 "Table",其中行在 HTML5/css3 中水平换行?

javascript - 如何在 ng-include 中调用内联方法并连接结果?