Skip to main content

CSS 阴影部分

CSS Shadow Parts 允许开发者在影子树内部的元素上设置 CSS 属性的样式。这对于自定义 Ionic Framework 影子 DOM 组件非常有用。

¥CSS Shadow Parts allow developers to style CSS properties on an element inside of a shadow tree. This is extremely useful in customizing Ionic Framework Shadow DOM components.

为什么是阴影部分?

¥Why Shadow Parts?

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

¥Ionic Framework is a distributed set of Web Components. Web Components follow the Shadow DOM specification in order to encapsulate styles and markup.

注意

Ionic Framework 组件并不都是 Shadow DOM 组件。如果该组件是 Shadow DOM 组件,则其 组件文档 的右上角会有一个徽章。Shadow DOM 组件的一个示例是 按钮组件

¥Ionic Framework components are not all Shadow DOM components. If the component is a Shadow DOM component, there will be a badge in the top right of its component documentation. An example of a Shadow DOM component is the button component.

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 组件渲染其影子树内部的元素,则不能使用 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 阴影部分

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

阴影部分解释

¥Shadow Parts Explained

阴影部分允许开发者从阴影树外部对阴影树内部进行样式设置。为此,可以使用 ::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 属性来将部件添加到影子树内部的元素中。它被添加到 Ionic Framework 中的组件中,不需要终端用户执行任何操作。

¥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() 伪元素允许开发者选择影子树内已通过部件属性公开的元素。

¥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.

除了能够定位零件之外,还可以在不显式暴露零件的情况下对 pseudo-elements 进行样式设置:

¥In addition to being able to target the part, pseudo-elements can be styled without them being explicitly exposed:

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

零件也适用于大多数 pseudo-classes

¥Parts work with most pseudo-classes, as well:

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

浏览器前缀的伪元素结构伪类 有一些已知的限制。

¥There are some known limitations with vendor prefixed pseudo-elements and structural pseudo-classes.

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:

  • 它是 影子 DOM 组件。如果是 范围 或 Light DOM 组件,则可以直接定位子元素。如果组件是 Scoped 或 Shadow,它将按其名称列在 组件文档页面 上。

    ¥It is a Shadow DOM component. If it is a Scoped or Light DOM component, the child elements can be targeted directly. If a component is Scoped or Shadow, it will be listed by its name on its component documentation page.

  • 它包含子元素。例如,ion-card-header 是一个 Shadow 组件,但所有样式都应用于宿主元素。由于它没有子元素,因此不需要部件。

    ¥It contains children elements. For example, ion-card-header is a Shadow component, but all styles are applied to the host element. Since it has no child elements, there’s no need for parts.

  • 子元素不是结构性的。在某些组件(包括 ion-title)中,子元素是用于定位内部元素的结构元素。我们不建议自定义结构元素,因为这可能会产生意想不到的结果。

    ¥The children elements are not structural. In certain components, including ion-title, the child element is a structural element used to position the inner elements. We do not recommend customizing structural elements as this can have unexpected results.

注意

我们欢迎对其他零件的建议。请求零件时,请创建包含尽可能多信息的 新的 GitHub 问题

¥We welcome recommendations for additional parts. Please create a new GitHub issue with as much information as possible when requesting a part.

已知限制

¥Known Limitations

浏览器支持

¥Browser Support

所有主要浏览器的最新版本都支持 CSS Shadow Parts。但是,某些旧版本不支持阴影部分。在应用中实现部件之前,请验证 浏览器支持 是否满足要求。如果需要浏览器支持旧版本,我们建议继续使用 CSS 变量 进行样式设置。

¥CSS Shadow Parts are supported in the recent versions of all of the major browsers. However, some of the older versions do not support shadow parts. Verify the browser support meets the requirements before implementing parts in an app. If browser support for older versions is required, we recommend continuing to use CSS Variables for styling.

浏览器前缀伪元素

¥Vendor Prefixed Pseudo-Elements

Vendor prefixed

pseudo-elements are not supported at this time. An example of this would be any of the ::-webkit-scrollbar pseudo-elements:

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

请参阅 这个问题在 GitHub 上 了解更多信息。

¥See this issue on GitHub for more information.

结构伪类

¥Structural Pseudo-Classes

大多数伪类都支持部件,但 结构伪类 不支持。下面是一个不起作用的结构伪类的示例。

¥Most pseudo-classes are supported with parts, however, structural pseudo-classes are not. An example of structural pseudo-classes that do not work is below.

/* 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.