CSS 阴影部分
CSS 阴影部分允许开发者样式化影子树(shadow tree)内元素的 CSS 属性。这对于自定义 Ionic Framework 的 Shadow DOM 组件非常有用。
为什么需要 Shadow Parts?
Ionic Framework 是一组分布式 Web Components。Web Components 遵循 Shadow DOM 规范以封装样式和标记。
Shadow DOM 有助于防止样式从组件中泄漏并不经意地应用到其他元素上。例如,我们为 ion-button 组件分配了一个 .button 类。如果没有 Shadow DOM 封装,如果用户在自己的某个元素上设置了 .button 类,它将继承 Ionic Framework 的按钮样式。由于 ion-button 是一个 Shadow 组件,这就不是问题了。
但是,由于这种封装,样式也无法渗透到 Shadow 组件的内部元素中。这意味着如果 Shadow 组件在其影子树内渲染元素,则无法直接使用 CSS 定位内部元 素。以 ion-select 组件为例,它渲染了以下标记:
<ion-select>
#shadow-root
<div class="select-text select-placeholder"></div>
<div class="select-icon"></div>
</ion-select>
占位符文本和图标元素位于 #shadow-root 内部,这意味着以下 CSS 将不能用于样式化占位符:
/* 不起作用 */
ion-select .select-placeholder {
color: blue;
}
那么如何解决这个问题呢?CSS 阴影部分!
阴影部分说明
阴影部分允许开发者从影子树外部样式化影子树内部。为此,part 必须被暴露,然后可以通过使用 ::part 进行样式化。
暴露 part
创建 Shadow DOM 组件时,可以通过在元素上分配 part 属性来向影子树内的元素添加 part。这是在 Ionic Framework 中添加到组件的,不需要最终用户执行任何操作。
继续以 ion-select 组件为例,标记更新如下:
<ion-select>
#shadow-root
<div part="placeholder" class="select-text select-placeholder"></div>
<div part="icon" class="select-icon"></div>
</ion-select>
上面显示了两个部分:placeholder 和 icon。有关其所有部分,请参阅 select 文档。
有了这些暴露的部分,现在可以使用 ::part 直接样式化元素。
::part 的工作原理
::part() 伪元素允许开发者选择影子树内已通过 part 属性暴露的元素。
由于我们知道 ion-select 暴露了一个 placeholder 部分用于在未选择值时样式化文本,我们可以按以下方式自定义它:
ion-select::part(placeholder) {
color: blue;
opacity: 1;
}
使用 ::part 进行样式化允许更改该元素接受的任何 CSS 属性。
除了能够定位 part 之外,伪元素也可以在没有显式暴露的情况下进行样式化:
ion-select::part(placeholder)::first-letter {
font-size: 22px;
font-weight: 500;
}
Parts 也适用于大多数 伪类:
ion-item::part(native):hover {
color: green;
}
Ionic Framework 部件
Ionic Framework 组件的所有已暴露部分都可以在其 API 页面的 CSS 阴影部分标题下找到。要查看所有组件及其 API 页面,请参阅组件文档。
要拥有部分,组件必须满足以下条件:
- 它是一个 Shadow DOM 组件。如果是 Scoped 或 Light DOM 组件,子元素可以直接定位。如果组件是 Scoped 或 Shadow,会在其组件文档页面上通过名称列出。
- 它包含子元素。例如,
ion-card-header是一个 Shadow 组件,但所有样式都应用于宿主元素。由于它没有子元素,因此不需要部分。 - 子元素不是结构性的。在某些组件中,包括
ion-title,子元素是用于定位内部元素的结构性元素。我们不建议自定义结构性元素,因为这可能导致意外结果。
我们欢迎对额外部分的建议。在请求部分时,请在 GitHub 上创建一个新的 issue,并尽可能提供详细信息。
已知限制
浏览器支持
CSS 阴影部分在所有主要浏览器的最新版本中都得到支持。但是,某些旧版本不支持阴影部分。在应用中实现部分之前,请验证浏览器支持是否满足要求。如果需要支持旧版本浏览器,我们建议继续使用 CSS 变量进行样式化。
厂商前缀伪元素
厂商前缀伪元素目前不受支持。例如,任何 ::-webkit-scrollbar 伪元素:
/* 不起作用 */
my-component::part(scroll)::-webkit-scrollbar {
background: green;
}
有关更多信息,请参阅 GitHub 上的这个问题。
结构伪类
大多数伪类都支持 parts,但结构伪类不支持。以下是不起作用的结构伪类示例。
/* 不起作用 */
my-component::part(container):first-child {
background: green;
}
/* 不起作用 */
my-component::part(container):last-child {
background: green;
}
链式 Parts
::part() 伪元素不能匹配额外的 ::part()。
例如,my-component::part(button)::part(label) 不匹配任何内容。这是因为这样做会暴露比预期更多的结构信息。
如果 <my-component> 的内部按钮使用类似 part="label => button-label" 的方式将按钮的内部部分转发到面板自己的 part 元素映射中,那么像 my-component::part(button-label) 这样的选择器将只选择该按钮的标签,忽略任何其他标签。