首页 > 编程笔记

CSS ::part的用法

下图展示的是 Chrome 浏览器中 input="range" 输入框元素的 Shadow DOM 结构。


图 1 范围选择框的Shadow DOM结构

其中有一个非标准的 HTML 属性 pseudo,点击这个元素,可以看到有一段浏览器内置的 CSS 规则,如下图所示。


图 2 范围选择框的自定义伪元素示意

其中的伪元素 ::-webkit-slider-runable-track 正好就是 pseudo 属性的属性值,而开发人员可以利用这个伪元素对浏览器内置的组件样式进行重置,如下图所示。


图 3 范围选择框的样式重置示意

为什么要这样设计呢?原因很简单,那就是默认情况下,Shadow DOM 外部的 CSS 代码是无法改变 Shadow DOM 内部元素的样式的。浏览器只能通过暴露某些私有属性或者私有伪元素的方法,让开发人员有机会在外部重置 Shadow DOM 元素的样式。

下面问题来了。浏览器默认的组件可以通过私有的伪元素暴露,那么 Web Components 自定义元素组件有没有办法暴露 Shadow DOM 中的元素,以便让开发人员可以从外部对组件进行样式设置呢?

有的,且方法还不止一个。其中一个方法就是使用 CSS 变量,另一个方法则是使用 ::part 伪元素,这个伪元素就是专门用来穿透 Shadow DOM 进行样式设置的。语法如下:
::part(xxx) {}
其中的参数 xxx 指的就是 Shadow DOM 元素的 part 属性值。

一例胜千言,还是以 <square-img> 这个例子示意,比方说我们希望可以在外部重置该组件内部图像和描述的样式,则可以给图像和描述对应的元素设置 part 属性,下面是具体的代码:
// 给图像元素设置part属性
var img = document.createElement('img');
img.setAttribute('part', 'img');
// 给描述元素设置part属性
var span = document.createElement('span');
span.setAttribute('part', 'span');
此时的 Shadow DOM 结构如下图所示。


图 4 part属性值设置示意

接下来,我们就可以使用 ::part 伪元素穿透 Shadow DOM 元素的上下文,对内部的元素进行样式设置了。例如:
square-img::part(img) {
    border-radius: 40% 40% 0 0;
}
square-img::part(span) {
    background-color: #cd0000;
}
给图像设置上圆角,给标题设置红色背景,此时就会有下图所示的效果,可以看到无论是图像的圆角还是标题的背景色都表现为预期的样式。


图 5 :part伪元素穿透shadow DOM元素的效果示意

::part伪元素对<slot>元素也是有效的

::part 伪元素不仅对常规的 HTML 元素有效,对 Web Components 中独有的 <slot> 元素也是有效的。

<slot> 元素可以看成一个占位符元素(国内多称之为“插槽元素”),可以把组件外部完成的 DOM 元素“替换”到 Shadow DOM 内部。

我们不妨继续使用 <square-img> 这个例子,这次我们在描述文字的前面插入一个 <slot> 元素,以便我们插入类似图标这样的前置元素。此时的 Shadow DOM 结构如下:
<img src="1.jpg" width="200" height="200">
<span>
    <slot name="prefix" part="prefix"></slot>注意图标尺寸和位置
</span>
而在页面中组件对应的 HTML 结构为:
<square-img src="1.jpg" alt="注意图标尺寸和位置">
    <i class="icon-info" slot="prefix"></i>
</square-img>
此时,.icon-info 这个元素就会作为 Shadow DOM 中 <slot> 元素的子元素显示。因此,若想设置 .icon-info 的样式,既可以直接针对选择器 .icon-info 进行设置,也可以借助 ::part 伪元素设置 <slot> 元素的样式,从而间接影响 .icon-info 元素的效果。这尤其适用于一些继承样式,例如颜色、字号、行高等。

假设 .icon-info 元素显示的是图标,则下面的CSS代码就可以控制图标的颜色和文字的右间距:
/* 直接对图标样式进行设置 */
.icon-info {
    display: inline-block;
    width: 1em; height: 1em;
    --mask: url(data:image/svg+xml;base64,...) no-repeat center / 100%;
    -webkit-mask: var(--mask);
    mask: var(--mask);
    background-color: currentColor;
    vertical-align: middle;
}
/* 通过<slot>元素微调图标样式 */
square-img::part(prefix) {
    display: inline-block;
    margin-right: .25rem;
    font-size: 14px;
}
最终实现的效果如下图所示。


图 6 ::part伪元素作用于<slot>元素示意

推荐阅读

副业交流群 关注微信公众号,加入副业交流群,学习变现经验,交流各种打法。