【问题标题】:Why doesn't DOM spec recognize flattened trees?为什么 DOM 规范不能识别扁平的树?
【发布时间】:2021-04-20 14:29:20
【问题描述】:

概括地说,DOM spec 定义了影子树的概念如下:

  • 影子树是与当前文档不同的节点树(light tree)。
  • 它的根(shadow root)通过host引用与光照树节点相连——这与后代/祖先关系不同DOM 树。
  • 此外,影子树可以有slot节点,它们又可以通过assigned节点slottables)连接/strong> 关系(再次被认为可与 DOM 树结构区分开来)。

我觉得这张照片有点牵强。看起来我们实际上没有两棵不同的树(光和影),而是三棵树。实际上,用户代理会渲染另一个名为 flattened tree 的用户代理。在这棵树中,影子根是其宿主的子节点,而插槽是其插槽表的祖先:

有人可能会争辩说,扁平化的树只是一个“虚拟想法”。但是,它用于 CSS 和事件侦听,可能这一事实证明了它的有用性。

松散地,影子树被视为影子主机的内容 而不是其正常的灯光树内容。然而,它的一些光 树的孩子可以通过分配它们被“拉入”阴影树 到插槽。 这会导致它们被视为插槽的子级 CSS 目的。 (CSS non-normative explanation)

当物质状态可以用单个扁平树来描述时,是否有任何理由引入影子树的概念,尤其是槽分配节点,其中节点与众所周知的后代/祖先引用相连,而不是具有特殊的引用命名为“分配节点”?当然,如果我们渴望封装,像 findElementById() 这样的 API 函数将保持扁平树的“影子”子树完好无损。

【问题讨论】:

标签: html shadow-dom


【解决方案1】:

TL;DR扁平化树是一个实现细节。

考虑一下这段代码sn-p

<html>
    <body>
        <style>
            my-element {
                font-style: italic;
                color: green;
            }

            p {
                color: darkorange;
            }
        </style>
        <p>Document Para</p>
        <my-element>
            <p>Light Child Para</p>
        </my-element>
    </body>
    <script>
        customElements.define('my-element', class extends HTMLElement {
            constructor() {
                super();
                this.attachShadow({mode: 'open'}).innerHTML =
                `
                    <p>Shadow Para</p>
                    <slot></slot>
                    <style>
                        :host > p {
                            color: brown;
                        }
                        slot {
                            color: blue;
                        }
                    </style>
                `;
            }
        });
        const shadowHost = document.querySelector('my-element');
        const shadowRoot = shadowHost.shadowRoot;
        const shadowPara = shadowRoot.querySelector('p');
        const slot = shadowRoot.querySelector('slot');
        const lightPara = document.querySelector('my-element > p');
        console.log(`Is Shadow Host the shadow Para's parent? ${shadowPara.parentNode === shadowHost}`);
        console.log(`Is Slot the Light Child Para's parent?${lightPara.parentNode === slot}`);
    </script>
</html>

考虑 Shadow DOM 中元素的样式。虽然主文档不能直接设置&lt;p&gt; 元素的样式,但从影子主机(在本例中为&lt;my-element&gt;)继承的属性(在本例中为font-stylecolor)由影子树元素继承。查看阴影树内&lt;p&gt; 元素的级联

选择器:host &gt; p 的规则&lt;p&gt; 元素视为直接后代 Shadow Host (my-element)。有效的词是treats&lt;p&gt; 元素在 DOM 树中不是后代。由于脚本中有此代码,请检查控制台中的输出

console.log(`Is Shadow Host the shadow Para's parent? ${shadowPara.parentNode === shadowHost}`);

还要注意fonty-stylecolor 的继承属性在&lt;p&gt; 的级联中被考虑。

&lt;slot&gt; 元素也是如此。尽管实际的&lt;slot&gt; 元素是Shadow Root 的直接后代,但开槽 元素(在本例中为&lt;p&gt;Light Child&lt;/p&gt;)并不是Shadow Root 的后代。查看开槽元素的级联。

该元素从主文档的样式表中获取colorspecified value,但它在级联中也具有color:blue 属性,该属性通过&lt;slot&gt; 元素上的样式来实现。同样,即使&lt;slot&gt; 元素在实际 DOM 中 不是光 Para 的父级,继承的属性也被处理为来自父级 &lt;slot&gt;

console.log(`Is Slot the Light Child Para's parent?${lightPara.parentNode === slot}`);
  • 正如您正确提到的,扁平树是使用的构造 在浏览器的 CSS 和事件引擎中,作为表示 建模节点上样式的父子关系,但它没有 帮助理解元素之间的 DOM 树关系。

  • 插槽分配不可避免地需要计算出来,因为 Light DOM 中的元素不会物理迁移到 Shadow DOM,只有对这些元素的引用保存在 Shadow DOM 的 &lt;slot&gt; 元素。

  • 扁平化树绝对是一个很好的可视化理解 样式部分,但将其作为 替换现有的 DOM 结构,例如 HTMLSlotElement.assignedNodes()

此外,规范正式points out 提及这一事实(强调我的)

松散地,影子树被视为影子主机的内容 而不是其正常的灯光树内容。然而,它的一些光 树的孩子可以通过分配它们被“拉入”阴影树 到插槽。这导致它们被视为插槽的子级 CSS 用途

... 不用于 DOM 目的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-28
    • 2011-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-13
    相关资源
    最近更新 更多