Web Components 正在快速的发展中,其提供了一些新的伪类与伪元素,用于在外部对组件样式进行调整。这篇文章就来聊一聊 Web Components 中新增的伪类与伪元素。

:defined

此伪类用于选中任何已定义的元素,包括任何浏览器内置的标准元素以及已成功定义的自定义元素。

假设有一个自定义元素如下:

<template id="my-button">
  <style>
    .my-btn {
      display: inline-block;
      line-height: 1;
      white-space: nowrap;
      cursor: pointer;
      text-align: center;
      box-sizing: border-box;
      outline: none;
      margin: 0;
      transition: .1s;
      font-weight: 500;
      padding: 12px 20px;
      font-size: 14px;
      border-radius: 4px;
      color: #fff;
      background-color: #409eff;
      border: 1px solid #409eff;
    }
  </style>
  <button class="my-btn">
    自定义按钮
  </button>
</template>
<script>
  class MyButton extends HTMLElement {
    constructor() {
      super();
      const shadow = this.attachShadow({mode: 'open'});
      const templateElm = document.getElementById('my-button');
      const content = templateElm.content.cloneNode(true);
      shadow.appendChild(content);
    }
  }

  customElements.define('my-button', MyButton);
</script>

其效果如下:

my_button

正常情况下,浏览器内置的标准元素当然都是已定义成功了的,而自定义元素就不一定的了,如果在自定义元素时有语法或其他任何错误,都会导致定义失败,此时页面上会出现一些难看的元素,而使用 :defined 伪类就可以将这些元素暂时隐藏起来:

my-button:not(:defined) {
  display: none;
}

my-button:defined {
  display: block;
}

这在当自定义元素在 CDN 上,而网速较慢时非常有用。

:host

此伪类用于选中 shadow DOM 的宿主。

沿用上例,如果我们希望该自定义元素的背景颜色由外部自定义,且不经过 DOM 操作,可以进行如下修改:

.my-btn {
  background-color: var(--background-color);
  border: solid 1px var(--background-color);
}

然后在外部样式中使用自定义元素的名称定义变量:

my-button {
  --background-color: #409eff;
}

这样做的确有效,但由于变量最初并没有值,可能会造成样式闪烁,影响体验,因此我们希望变量有初始值。

那么,如何设置初始值呢?当自定义元素时,在 shadow DOM 内部的样式中是无法使用自定义元素名称的:

my-button {
  --background-color: #409eff; /*无效*/
}

此时便可以使用 :host 伪类,在自定义元素内部样式中:

:host {
  --background-color: #409eff;
}

:host 伪类还可以指定带有特定选择器的 shadow DOM 宿主。

沿用上例,假设我们希望当 my-button 带有 .danger 类名时按钮的背景色为红色,则可以这样写:

:host {
  --background-color: #409eff;
}

:host(.danger) {
  --background-color: #f56c6c;
}

使用时:

<my-button class="danger"></my-button>

得到的效果如下:

my_button_danger

或者可以将 danger 作为属性,而不是类名:

:host([danger]) {
  --background-color: #f56c6c;
}

使用时:

<my-button danger></my-button>

得到的效果是一样的。

:host-context()

此伪类用于选中作为特定选择器后代的 shadow DOM 宿主,注意它和 :host 伪类的区别,:host 伪类选中的是自身带有特定选择器的宿主。

什么意思呢?假设我们希望当前文中的自定义元素是 form 的后代时,背景色为绿色,则可以这样写:

:host-context(form) {
  --background-color: #67c23a;
}

使用时:

<form action="">
  <my-button danger></my-button>
</form>

上述代码得到的结果仍然是一个红色背景的按钮,示例引出了一个优先级问题,当同时出现 :host():host-context() 时,它们的优先级由各自指定的选择器优先级决定。

在示例中,:host([danger]) 中使用的是属性选择器,而 :host-context(form) 中使用的是标签选择器,因此前者优先级更高(注意,:host-context() 必须有特定选择器,否则是无效的)。

想让示例呈现出期望的效果,可以再使用后代选择器:

:host-context(form) .my-btn{
  --background-color: #67c23a;
}

得到的效果如下:

my_button_form

或者为 form 元素添加属性或类名,使用的相应选择器。

在创建某些可能会产生嵌套效果的自定义元素(诸如表单元素)时, :host-context() 可以让嵌套的元素在样式上实现统一设置,非常方便。

::slotted()

此伪元素用于选中通过 <slot> 插入到 shadow DOM 中的元素。

如果 Web Components 仅仅只能将元素封装为独立组件,其扩展性将大打折扣,因此其提供了 <slot> 元素,用于向自定义元素中插入元素。对于这些插入的元素,则可以使用 ::slotted() 伪元素进行选中,进而进行样式调整。

沿用上例,假设要在按钮中插入一个图标,可以这样实现,在模版中定义 <slot>

<button type="button" class="my-btn">
  <slot name="prefix"></slot>
  自定义按钮
</button>

使用时:

<my-button class="danger" danger><i slot="prefix" class="fa fa-edit"/></my-button>

得到的结果如下:

my_button_slot

此时如果想要修改图标的颜色,则可以在 shadow DOM 内部:

::slotted(*) {
  color: blueviolet;
}

得到的结果如下:

my_button_icon

示例中使用了通配符作为选择器,对任何插入到 shadow DOM 中的元素都生效,使用类选择器等还可以实现更精确的控制(和 :host-context 一样,:slotted() 伪元素也必须指定特定选择器,否则无效)。

结语

这些新增的伪类与伪元素对于创建高扩展性的自定义元素来说非常有用,虽然兼容性在现阶段是一个障碍,但仍然值得尝试。

最近更新:
作者: MeFelixWang