Skip to main content

CSS 阴影部分

CSS Shadow Parts 允许开发者对 shadow tree 内的元素的 CSS 属性进行样式设置。这在定制 Ionic Framework Shadow DOM 组件时非常有用。

为什么是阴影部分?

🌐 Why Shadow Parts?

Ionic 框架是一套分布式的 Web 组件。Web 组件遵循 Shadow DOM 规范 以封装样式和标记。

note

Ionic Framework 组件并非全部是 Shadow DOM 组件。如果某组件是 Shadow DOM 组件,其组件文档的右上角会有一个标识。Shadow DOM 组件的一个例子是按钮组件

Shadow DOM 对防止样式从组件中泄漏并无意地应用到其他元素非常有用。例如,我们将 .button 类分配给我们的 ion-button 组件。如果没有 Shadow DOM 封装,如果用户在他们自己的某个元素上设置 .button 类,该元素会继承 Ionic Framework 按钮样式。由于 ion-button 是一个 Shadow 组件,因此这不是问题。

🌐 Shadow DOM is useful for preventing styles from leaking out of components and unintentionally applying to other elements. For example, we assign a .button class to our ion-button component. Without Shadow DOM encapsulation, if a user were to set the class .button on one of their own elements, it would inherit the Ionic Framework button styles. Since ion-button is a Shadow component, this is not a problem.

然而,由于这种封装,样式也无法渗透到 Shadow 组件的内部元素中。这意味着如果一个 Shadow 组件在其 shadow 树中渲染了元素,内部元素无法直接使用 CSS 进行定位。以 ion-select 组件为例,它渲染了以下标记:

🌐 However, due to this encapsulation, styles aren’t able to bleed into inner elements of Shadow components either. This means that if a Shadow component renders elements inside of its shadow tree, the inner elements cannot be targeted directly with CSS. Using the ion-select component as an example, it renders the following markup:

<ion-select>
#shadow-root
<div class="select-text select-placeholder"></div>
<div class="select-icon"></div>
</ion-select>

占位符文本和图标元素位于 #shadow-root 内,这意味着以下 CSS 不会 用于为占位符设置样式:

🌐 The placeholder text and icon elements are inside of the #shadow-root, which means the following CSS will NOT work to style the placeholder:

/* Does NOT work */
ion-select .select-placeholder {
color: blue;
}

那么我们如何解决这个问题?CSS Shadow Parts

🌐 So how do we solve this? CSS Shadow Parts!

阴影部分解释

🌐 Shadow Parts Explained

Shadow parts 允许开发者从 shadow tree 外部对其内部进行样式设置。为了做到这一点,part 必须被公开,然后可以使用 ::part 进行样式设置。

🌐 Shadow parts allow developers to style inside a shadow tree, from outside of that shadow tree. In order to do so, the part must be exposed and then it can be styled by using ::part.

暴露部分

🌐 Exposing a part

在创建 Shadow DOM 组件时,可以通过在元素上分配 part 属性,将一个部分添加到 shadow tree 内的元素上。这在 Ionic 框架中添加到组件中,且不需要终端用户采取任何操作。

🌐 When creating a Shadow DOM component, a part can be added to an element inside of a shadow tree by assigning a part attribute on the element. This is added to the component in Ionic Framework and requires no action from an end user.

继续以 ion-select 组件为例,标记已更新如下所示:

🌐 Continuing to use the ion-select component as an example, the markup is updated to look like the following:

<ion-select>
#shadow-root
<div part="placeholder" class="select-text select-placeholder"></div>
<div part="icon" class="select-icon"></div>
</ion-select>

上面显示了两个部分:placeholdericon。有关其所有部分,请参阅选择文档

🌐 The above shows two parts: placeholder and icon. See the select documentation for all of its parts.

当这些部分被暴露时,现在可以使用 ::part 直接对元素进行样式设置。

🌐 With these parts exposed, the element can now be styled directly using ::part.

::part 工作原理

🌐 How ::part works

::part() 伪元素允许开发者选择通过 part 属性公开的阴影树内部的元素。

🌐 The ::part() pseudo-element allows developers to select elements inside of a shadow tree that have been exposed via a part attribute.

既然我们知道 ion-select 在没有选择值时会显示一个 placeholder 部分用于文本样式,我们可以按以下方式进行自定义:

🌐 Since we know that ion-select exposes a placeholder part for styling the text when there is no value selected, we can customize it in the following way:

ion-select::part(placeholder) {
color: blue;
opacity: 1;
}

使用 ::part 进行样式设置允许更改该元素接受的任何 CSS 属性。

🌐 Styling using ::part allows any CSS property that is accepted by that element to be changed.

除了能够定位到某个部分外, 伪元素 也可以在不显式暴露它们的情况下进行样式设置:

ion-select::part(placeholder)::first-letter {
font-size: 22px;
font-weight: 500;
}

部件也适用于大多数 伪类

ion-item::part(native):hover {
color: green;
}
note

供应商前缀伪元素结构伪类方面存在一些已知的限制。

Ionic 框架部分

🌐 Ionic Framework Parts

Ionic Framework 组件的所有可暴露部分都可以在其 API 页面上的 CSS Shadow Parts 标题下找到。要查看所有组件及其 API 页面,请参见 组件文档

🌐 All exposed parts for an Ionic Framework component can be found under the CSS Shadow Parts heading on its API page. To view all components and their API pages, see the Component documentation.

为了获得零件,组件必须满足以下标准:

🌐 In order to have parts a component must meet the following criteria:

  • 它是一个Shadow DOM组件。如果它是一个Scoped或Light DOM组件,子元素可以被直接定位。如果一个组件是Scoped或Shadow,它将在其组件文档页面上以名称列出。
  • 它包含子元素。例如,ion-card-header 是一个 Shadow 组件,但所有样式都应用于宿主元素。由于它没有子元素,因此不需要 parts。
  • 子元素不是结构性的。在某些组件中,包括 ion-title,子元素是用于定位内部元素的结构性元素。我们不建议自定义结构性元素,因为这可能会产生意想不到的结果。
note

我们欢迎对额外零件的推荐。在请求零件时,请尽可能提供详细信息,并创建一个 新的 GitHub 问题

已知限制

🌐 Known Limitations

浏览器支持

🌐 Browser Support

在所有主流浏览器的最新版本中,CSS Shadow Parts 都受到支持。然而,一些旧版本不支持 shadow parts。在在应用中实现 parts 之前,请验证 浏览器支持 是否满足要求。如果需要支持旧版本浏览器,我们建议继续使用CSS 变量进行样式设计。

浏览器前缀伪元素

🌐 Vendor Prefixed Pseudo-Elements

浏览器前缀的 伪元素目前不受支持。一个例子是任何 ::-webkit-scrollbar 伪元素:

/* Does NOT work */
my-component::part(scroll)::-webkit-scrollbar {
background: green;
}

请参阅 GitHub上的这个问题 以获取更多信息。

结构伪类

🌐 Structural Pseudo-Classes

大多数伪类可以与部分元素一起使用,然而, 结构性伪类 不行。下面是一个不适用的结构性伪类示例。

/* Does NOT work */
my-component::part(container):first-child {
background: green;
}

/* Does NOT work */
my-component::part(container):last-child {
background: green;
}

链接部分

🌐 Chaining Parts

::part() 伪元素无法匹配额外的 ::part()

🌐 The ::part() pseudo-element can not match additional ::part()s.

例如,my-component::part(button)::part(label) 不匹配任何内容。这是因为这样做会暴露比预期更多的结构信息。

🌐 For example, my-component::part(button)::part(label) does not match anything. This is because doing so would expose more structural information than is intended.

如果 <my-component> 的内部按钮使用类似 part="label => button-label" 的东西将按钮的内部部件转发到面板自己的部件元素映射中,那么像 my-component::part(button-label) 这样的选择器将只选择那个按钮的标签,而忽略其他标签。

🌐 If the <my-component>’s internal button uses something like part="label => button-label" to forward the button’s internal parts up into the panel’s own part element map, then a selector like my-component::part(button-label) would select just the one button’s label, ignoring any other labels.