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

Posted

技术标签:

【中文标题】如何获取包含 shadowRoot 元素的文档或节点中的所有 HTML【英文标题】:How can I get all the HTML in a document or node containing shadowRoot elements 【发布时间】:2021-12-20 08:46:14 【问题描述】:

我还没有看到这个问题的满意答案。这基本上是this question 的副本,但它被错误地关闭并且给出的答案不够充分。

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

这对于网页抓取很有用,或者在我的情况下,在处理自定义元素的 javascript 库上运行测试。我确保它产生了我想要的输出,然后我使用这个函数为给定的测试输出抓取 html,并使用复制的 HTML 作为 expected 输出来比较未来的测试.

【问题讨论】:

【参考方案1】:

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

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
    

【讨论】:

【参考方案2】:

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

然后,您可以使用 something 进入 元素和 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 是没有意义的;您将失去所有正确的样式。

【讨论】:

你能解释一下这个函数应该如何使用吗?你应该将什么传递给“匹配”? 它需要一个与 inside 每个 shadowRoot 的内容匹配的 selector 是的,我得到了 el 和 selector,但你还没有解释应该是什么匹配,所以我不能使用它。 您可以指定自己的函数来“操作”内部 shadowroots 好的,你的代码不是问题的答案。

以上是关于如何获取包含 shadowRoot 元素的文档或节点中的所有 HTML的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 shadow dom 创建聚合物自定义元素,以便可以访问它的 shadowRoot?

每日思考(2019/12/10)

shadowRoot.getSelection()?

shadow dom 隔离代码 封装

section+display

行内元素与块级元素