如何获取包含shadowRoot元素的文档或节点中的所有HTML

How can I get all the HTML in a document or node containing shadowRoot elements

提问人:Moss 提问时间:11/7/2021 更新时间:11/7/2021 访问量:2475

问:

对于这个问题,我还没有看到令人满意的答案。这基本上是这个问题的重复,但它被不恰当地关闭,给出的答案不够充分。

我已经提出了我自己的解决方案,我将在下面发布。

这对于网络抓取很有用,或者就我而言,在处理自定义元素的 javascript 库上运行测试。我确保它产生我想要的输出,然后我使用这个函数来抓取给定测试输出的 HTML,并使用复制的 HTML 作为预期的输出,以便将来将测试进行比较。

JavaScript 网页抓取 shadow-dom 自定义元素 native-web-component

评论


答:

5赞 Moss 11/7/2021 #1

这是一个可以执行请求的函数。请注意,它忽略了 html 注释和其他边缘内容。但它使用 shadowRoots 检索常规元素、文本节点和自定义元素。它还处理插槽模板内容。它尚未经过详尽的测试,但似乎可以很好地满足我的需求。

像 or 一样使用它。extractHTML(document.body)extractHTML(document.getElementByID('app'))

function extractHTML(node) {
            
    // return a blank string if not a valid node
    if (!node) return ''

    // if it is a text node just return the trimmed textContent
    if (node.nodeType===3) return node.textContent.trim()

    //beyond here, only deal with element nodes
    if (node.nodeType!==1) return ''

    let html = ''

    // clone the node for its outer html sans inner html
    let outer = node.cloneNode()

    // if the node has a shadowroot, jump into it
    node = node.shadowRoot || node
    
    if (node.children.length) {
        
        // we checked for children but now iterate over childNodes
        // which includes #text nodes (and even other things)
        for (let n of node.childNodes) {
            
            // if the node is a slot
            if (n.assignedNodes) {
                
                // an assigned slot
                if (n.assignedNodes()[0]){
                    // Can there be more than 1 assigned node??
                    html += extractHTML(n.assignedNodes()[0])

                // an unassigned slot
                } else { html += n.innerHTML }                    

            // node is not a slot, recurse
            } else { html += extractHTML(n) }
        }

    // node has no children
    } else { html = node.innerHTML }

    // insert all the (children's) innerHTML 
    // into the (cloned) parent element
    // and return the whole package
    outer.innerHTML = html
    return outer.outerHTML
    
}
0赞 Danny '365CSI' Engelman 11/7/2021 #2

只有当使用该设置创建 shadowRoots 时,您才能从外部访问 shadowRoots。mode:"open"

然后,您可以使用以下内容深入了解元素和 shadowRoots:

 const shadowDive = (
          el, 
          selector, 
          match = (m, r) => console.warn('match', m, r)
  ) => {
    let root = el.shadowRoot || el;
    root.querySelector(selector) && match(root.querySelector(selector), root);
    [...root.children].map(el => shadowDive(el, selector, match));
  }

注意:如果 Web 组件样式基于 shadowDOM 行为,则提取原始 HTML 是没有意义的;您将失去所有正确的样式。

评论

0赞 Moss 11/8/2021
您能解释一下这个功能应该如何使用吗?你应该传递什么来“匹配”?
0赞 Danny '365CSI' Engelman 11/9/2021
它需要一个与每个 shadowRoot 的内容匹配的内容selector
0赞 Moss 11/10/2021
是的,我得到了 el 和选择器,但你仍然没有解释应该是什么匹配,所以我不能使用它。
0赞 Danny '365CSI' Engelman 11/10/2021
您可以指定自己的函数来“操作”shadowroots中的内容
1赞 Danny '365CSI' Engelman 11/10/2021
这个函数让你进入一个影子根,在那里你可以做任何你想做的事。喜欢抓取 HTML