插槽(Slots)
在Shadow DOM里,Slots占着很大的比重。
在Shadow DOM里,Slots占着很大的比重。 - https://developers.google.com/web/fundamentals/getting-started/primers/shadowdom#slots
在创建自定义组件时候,我们希望能够提供进入特定组件唯一的标记,我们想要和组件开发者一样使用/group/style。
组件用户提供的DOM称之为light DOM
,slots
提供了一种我们能够随意调整标记的方式。根据不同的使用场景来组织这些样式。
插槽有两个方面的内容。
- Light DOM Elements : 插槽入口
<custom-picture>
<!--Light DOM <img> saying it should be put into the "profile-picture" slot-->
<img src="assets/user.svg" slot="profile-picture">
</custom-picture>
- The Actual Slot :
<slot>
元素, 存在shadow DOM内部,可以通过一个属性名被Light DOM 找到。
<custom-picture>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#shadow-root
<slot name="profile-picture">
<!--The <img> from the light DOM gets rendered here!-->
</slot>
_________________________________________
划重点啦: Light DOM 和 slots是如何在一起工作的。
如果
引入了该元素,则这些元素可“跨越” shadow DOM 的边界。 这些元素称为分布式节点。从概念上来看,分布式节点似乎有点奇怪。 Slot 实际上并不移动 DOM;它们在 shadow DOM 内部的其他位置进行渲染。 - https://developers.google.com/web/fundamentals/getting-started/primers/shadowdom#slots
如果我不给 <custom-picture>
中的<img>
元素添加‘slot’属性会怎样呢?
没有东西会被渲染出来的,为什么呢?
- 一个包含有shadow DOM的宿主元素,仅仅渲染位于 shadow DOM里面的内容
- 为了渲染 Light DOM 元素,需要把它变成为 shadow DOM的一部分
- 我们把light DOM 元素放进slots是为了让他们成为 shadow DOM的一部分
- 在上述例子中,没有出现这样的元素,叫
profile-picture
的插槽 - 因为没有这样的元素,所以Light DOM 的
<img>
不会被渲染 - 被命名的插槽仅容纳那些指定它们要进入特定槽的Light DOM元素
如果我想要渲染所有的元素,但不指明元素应该显示在哪里,怎么做?
这就需要我们在shadow DOM有这样一个通用的没有名字的插槽。例如-
<custom-picture>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#shadow-root
<!--General purpose slot, render every element from light DOM that doesn't mention a slot name, here.-->
<slot>
<!--The <img> from the light DOM gets rendered here!-->
</slot>
_________________________________________
<!--Light DOM-->
<img src="assets/user.svg">
</custom-picture>
如果我添加了两个未命名插槽呢?
Woah! ?
事实上,我们可以拥有多个未命名插槽,甚至是已命名插槽,但是这些命名基本没什么用处。Llight DOM元素会匹配第一个slot。
<custom-picture>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#shadow-root
<slot>
<!--The <img> from the light DOM gets rendered here! Winner!-->
</slot>
<slot>
<!--Doesn't come here!-->
</slot>
_________________________________________
<!--Light DOM-->
<img src="assets/user.svg">
</custom-picture>
如果存在一个没有 Light DOM 元素匹配的插槽呢?
不会渲染任何元素的,除非插槽提供一个回退内容,然后这样应该怎么实现呢?
#shadow-root
<slot name="nobody-comes-here">
<h1> I'll show up when noone does!</h1>
</slot>
<style>
/* And that fallback can be styled from within the shadow DOM just like we do styles*/
slot[name="nobody-comes-here"] h1{
color: #bada55;
}
</style>
什么是插槽元件,我们如何给他们添加样式?
这类插槽元素可以使用 ::slotted()
函数性伪类元素设置样式,语法如下。
::slotted(<compound-selector >) {
/* styles */
}
Example -
<custom-picture>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#shadow-root
<slot>
<!--The <img> from the light DOM gets rendered here!-->
</slot>
<style>
/* find the slotted image and set their width and height */
::slotted(img) {
width: 256px;
height: 256px;
}
</style>
_________________________________________
<!--Light DOM-->
<img src="assets/user.svg">
</custom-picture>
这是文档中的相关定义
The ::slotted() pseudo-element represents the elements assigned, after flattening, to a slot. This pseudo-element only exists on slots.
扁平化树结构参考 here.
需要记住的一件重要的事是: 仅仅是宿主元素的第一子元素才可以分配给一个插槽,例如
<custom-picture>
<div class="picture-wrapper">
<!--This won't work! Slots can't pick descendants out of the host element's light DOM tree and put them in.-->
<img src="assets/user.svg" slot="assign-me" />
</div>
</custom-picture>
I, 然而,我不知道(译者注:别的子元素为什么不可以分配给插槽)以及背后的原理是什么。所以我提了一个bug: this bug;
如何向下传递 Light DOM元素呢?
这听起来好像我们不需要考虑这种情况,但是这经常是必要的。
<parent-element>
<!--parent-element uses child-element in it's shadow DOM and we want this span to render inside that child-element's shadow DOM-->
<span slot="parent-slot">Finally</span>
</parent-element>
这是我们创建的自定义元素:
class ParentElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<!--We specify a slot property on the slot itself. Which specifies where it goes in the child-element's shadow DOM-->
<child-element>
<slot name="parent-slot" slot="child-slot"></slot>
</child-element>
`;
}
}
class ChildElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<slot name="child-slot">
<!--The span with the thext "Finally" gets rendered here!-->
</slot>
`;
}
}
window.customElements.define('parent-element', ParentElement);
window.customElements.define('child-element', ChildElement);
插槽也提供了JavaScript API
- 找出插槽中的元素 -
slot.assignedNodes()
; - 找出某个Light DOM被赋值到哪个插槽 -
element.assignedSlot
;