> For the complete documentation index, see [llms.txt](https://lizh.gitbook.io/knowledge/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lizh.gitbook.io/knowledge/css-xi-lie/08-chong-xin-ren-shi-wei-lei-yu-wei-yuan-su.md).

# 重新认识伪类与伪元素

伪类、伪元素，都是属于CSS选择器的范围。

css 引入伪类和伪元素概念是为了格式化文档树以外的信息。也就是说，**伪类和伪元素是用来修饰不在文档树中的部分**，比如，一句话中的第一个字母，或者是列表中的第一个元素。

## 伪类是啥？

伪类是一个选择处于**特定状态的元素的选择器**。比如：`:first-child`、`:hover`，当元素是当前类型的第一个元素或者鼠标指针悬浮在该元素时，它们表现**就像是在该元素上加一个类一样**。

**伪类以开头为单冒号`:`。**

### 简单伪类示例

有人也称这种选择器为 CSS3 结构类。常用的伪类有：

* E:fist-child：选中父元素的第1个子元素，且该子元素为 E 类型。比如：`p:first-child`，配置当前元素的父元素下的第一个且类型为 `p` 的元素。如果第一个元素不是 `p`，则失效。
* E:last-child：选中父元素的最后一个子元素，且该子元素为 E 类型。
* E:nth-child()：选中父元素的第 n 个子元素，且该子元素为 E 类型。
* E:nth-last-child()：同上，不过是从后开始查找；
* E:nth-of-type()：选中父元素下第 n 个 E 类型元素。比如：`p:nth-of-type(1)`，在父素元素下查找子元素，直到找到第一个 `p` 元素。
* E:nth-last-of-type()：同上，不过是从后开始查找；
* E:first-of-type：选中父元素下第一个 E 类型元素；
* E:last-of-type：选中父元素下最后一个 E 类型元素；
* E:only-child：选中父元素下唯一的子元素，且该子元素为 E 类型；
* E:only-of-type：如果父元素下只有一个 E 类型子元素，则选中该子元素；
* E:empty：选中不包含子元素和内容的 E 类型元素；
* :blank：匹配没有子节点、仅有空的文本节点、仅有空白符的文本节点。？？
* :playing：匹配代表音频、视频或者相似的能“播放”或者“暂停”的资源的，且正在“播放”的元素。
* :paused：匹配代表音频、视频或者相似的能“播放”或者“暂停”的资源的，且正在“暂停”的元素。
* :root：匹配文档的根元素。**除了优先级更高之外，与 `html` 选择器相同**。
* :scope：匹配任何为参考点元素的的元素。
* :target：匹配当前URL目标的元素（例如如果它有一个匹配当前URL分段的元素）。
* :is()：匹配传入的选择器列表中的任何选择器。
* :not：匹配作为值传入自身的选择器未匹配的物件。

#### :nth-child(an+b)

找到所有当前元素的兄弟元素，然后按照位置先后顺序从1开始排序，选择的结果为CSS伪类:nth-child括号中表达式（an+b）匹配到的元素集合（n=0，1，2，3...）。

* `0n+3` 或简单的 `3` 匹配第三个元素。
* `1n+0` 或简单的 `n` 匹配每个元素。（兼容性提醒：在 Android 浏览器 4.3 以下的版本 `n` 和 `1n` 的匹配方式不一致。`1n` 和 `1n+0` 是一致的，可根据喜好任选其一来使用。）
* `2n+0` 或简单的 `2n` 匹配位置为 2、4、6、8...的元素（n=0时，2n+0=0，第0个元素不存在，因为是从1开始排序)。你可以使用关键字 **`even`** 来替换此表达式。
* `2n+1` 匹配位置为 1、3、5、7...的元素。你可以使用关键字 **`odd`** 来替换此表达式。
* `3n+4` 匹配位置为 4、7、10、13...的元素。

`a` 和 `b` 都必须为整数，并且元素的第一个子元素的下标为 1。换言之就是，该伪类匹配所有下标在集合 { an + b; n = 0, 1, 2, ...} 中的子元素。另外需要特别注意的是，`an` 必须写在 `b` 的前面，不能写成 `b+an` 的形式。

选择器示例：

* `tr:nth-child(2n+1)`、`tr:nth-child(odd)`：表示HTML表格中的奇数行。
* `tr:nth-child(2n)`、`tr:nth-child(even)`：表示HTML表格中的偶数行。
* `span:nth-child(0n+1)`：表示子元素中第一个且为span的元素，与 [`:first-child`](https://developer.mozilla.org/zh-CN/docs/Web/CSS/:first-child) 选择器作用相同。
* `span:nth-child(1)`：表示父元素中子元素为第一的并且名字为span的标签被选中
* `span:nth-child(-n+3)`：匹配前三个子元素中的span元素。

#### :scope

当在样式表中使用时，`:scope` 等效于 `:root`，因为目前尚无一种方法来显式建立作用域元素。

当从 DOM API 使用，如：`querySelector(), querySelectorAll(), matches(), 或 Element.closest()`，`:scope` 匹配你调用API的元素。？？

#### :target

代表一个唯一的页面元素(目标元素)，其`id` 与当前URL片段匹配 。

如：当前页面URL是 `http://www.example.com/index.html#section2` ，`hash`值为`section2`，`:target` 指向ID为 `section2` 的页面元素。

#### :is()

将选择器列表作为参数，并选择该列表中任意一个选择器可以选择的元素。这对于以更紧凑的形式编写大型选择器非常有用。

```css
/*element_box下所有p元素*/
.element_box :is(p) {
}

/*element_box下所有class为red的p元素*/
.element_box :is(p):is(.red) {
}

/*element_box下所有的b元素和em元素*/
.element_box :is(b, em) {
}
```

#### :not

用来匹配不符合一组选择器的元素。由于它的作用是防止特定的元素被选中，它也被称为反选伪类（negation pseudo-class）。

```css
/*element_box下所有的非p元素*/
.element_box :not(p) {
}

/*element_box下所有的非p元素和class不为red的元素*/
.element_box :not(p):not(.red) {
}
```

**not不支持复杂选择器：**

`:not()`逻辑伪类出身很早，早到IE9浏览器都支持，不像现在的新出来的逻辑选择器，**`:not()`伪类括号里面并不支持复杂的选择器**（虽然新的规范已经让支持了，目前还没有浏览器跟进）。

```css
/*:not()伪类括号里面不能多个选择器：*/
.element_box :not(.green, .red) {}

/*可以写成*/
.element_box :not(.green),
.element_box :not(.red) {}
```

```css
/*:not()伪类括号里面不支持选择器级联：*/
:not(.element_box .green) {}

/*可以写成*/
:not(.element_box):not(.green) {}
```

**not容易搞不清楚的否定逻辑关系：**

* 匹配既不包含`.green`类名，又不包含`.red`类名的元素：

  ```css
  /*错误*/
  .element_box :not(.green),
  .element_box :not(.red) {}

  /*正确*/
  .element_box :not(.green):not(.red) {}
  ```

逗号分隔的选择器，表示的是“或”的关系，而不是“与”的关系。`:not(.green), :not(.red)` 含义是不包含`.green`类名的元素，或者不包含`.red`类名的元素。最后导致的结果是`.green`类名和`.red`类名元素都会匹配。

### 动态伪类

一些伪类只会在用户以某种方式和文档交互的时候应用。这些叫做**动态伪类**，表现得就像是一个类在用户和元素交互的时候加到了元素上一样。

动态伪类包含两种：

* 锚点伪类。如：`:link、:visited`
* 用户行为伪类。如：`:hover、:active、:focus`

对于锚点下的四个伪类的设置，有一点需要特别注意，那就是他们的先后顺序，要让他们遵守一个爱恨原则 **LVHA**（LoVe/HAte） ，也就是 `Link--visited--hover--active`。

* :link：匹配未曾访问的链接。
* :visited：匹配已访问链接。
* :hover：用于当用户把鼠标移动到元素上面时的效果；
* :active：用于用户点击元素那一下的效果（正发生在点的那一下，松开鼠标左键此动作也就完成了）
* :focus：用于元素成为焦点，这个经常用在表单元素上。
* :any-link：匹配一个链接的 :link 和 :visited 状态。

### UI元素状态伪类

对于 HTML 中的 Form 元素状态的伪类，称为 UI 元素状态伪类。

主要有：

* :enabled：可写状态。如：`type="text"`；
* :disabled：不可写状态。如：`type="text"`；
* :checked：是否选中状态。如：`type="radio"`、`type="checkbox"`；
* :optional：匹配不是必填的 form 元素；
* :placeholder-shown：匹配显示占位文字的 input 元素；
* read-only：匹配用户不可更改的元素；
* :read-write：匹配用户可更改的元素；
* :required：匹配必填的 form 元素。

## 伪元素是啥？

伪元素用于\*\*创建一些不在文档树中的元素，并为其添加样式，\*\*而不是在现有的元素上加类。比如说，我们可以通过 `:before` 来在一个元素前增加一些文本，并为这些文本添加样式。虽然用户可以看到这些文本，但是这些文本实际上不在文档树中。

**伪元素开头为双冒号`::`。**

**伪元素例表：**

* ::after：匹配出现在原有元素的实际内容之后的一个可样式化元素。
* ::before：匹配出现在原有元素的实际内容之前的一个可样式化元素。
* ::first-letter：匹配元素的第一个字母。
* ::first-line：匹配包含此伪元素的元素的第一行。
* ::grammar-error：匹配文档中包含了浏览器标记的语法错误的那部分。
* ::selection：匹配文档中被选择的那部分。
* ::spelling-error：匹配文档中包含了浏览器标记的拼写错误的那部分。
* :: cue：匹配所选元素中的WebVTT提示。这可以用于在VTT轨道的媒体中使用字幕和其他线索。
* ::backdrop：匹配在任何处于 **全屏模式** 的元素下的即刻渲染的盒子（并且在所有其他在堆中的层级更低的元素之上），比如：`video` 点击播放，处理全屏状态时。
* ::marker：匹配如`ul li` 中项目符号或者数字。它作用在任何设置了 `display: list-item` 的元素或伪元素。
* ::placeholder：匹配一个表单元素的占位文本，它允许开发者和设计师自定义占位文本的样式。
* ::slotted()：用于选定那些被放在 HTML模板 中的元素。

### 生成带有::before和::after的内容

这是一组特别的伪元素，它们和 `content` 属性一同使用，使用CSS将内容插入到你的文档中中。

```css
.element_box::after {
    display: inline-block;
    width: 400px;
    content: '我是一个伪元素';
    color: blue;
    font-size: 24px;
    font-weight: bold;
}
```

`content` 可以为以下值：

* none：不会产生伪类元素；
* normal：`:before` 和 `:after` 伪类元素中会被视为 `none`；
* `<string>`：文本内容；
* `<uri> url()`：URI 值会指定一个外部资源（比如图片）。如果该资源或图片不能显示，它就会被忽略或显示一些占位（比如无图片标志）；
* `<counter>`：计数器可以指定两种不同的函数：`counter()` 或 `counters()`；
* `attr(X)`：将元素的X属性以字符串形式返回。如果该元素没有 X 属性，则返回一个空字符串。区分大小写的属性返回值依赖文挡的语言设定。

## 伪类和伪元素的区别？

* **表示方法**

  CSS2 中伪类、伪元素都是以单冒号`:`表示，CSS2.1 后规定伪类用单冒号表示，伪元素用双冒号`::`表示，浏览器同样接受 CSS2 时代已经存在的伪元素的单冒号写法。

  对于 CSS2 之后所有新增的伪元素(如：`::selection`)，**应该采用双冒号的写法。**

  **单冒号和双冒号兼容的伪元素：**

  * :after、::after；
  * :before、::before；
  * :cue 、::cue；
  * :first-letter、::first-letter；
  * :first-line、::first-line。
* **定义不同**

  伪类即假的类，通常可以添加类来达到效果；伪元素即假元素，需要通过添加元素才能达到效果。

  ```html
  <section class="box box_01">
      <p>江南可采莲，莲叶何田田。</p>
      <p>鱼戏莲叶间。</p>
      <p>鱼戏莲叶东，鱼戏莲叶西，鱼戏莲叶南，鱼戏莲叶北。</p>

      <span>玉炉香，红蜡泪，偏照画堂秋思。眉翠薄，鬓云残，夜长衾枕寒。</span>
      <span>梧桐树，三更雨，不道离情正苦。一叶叶，一声声，空阶滴到明。</span>
      <em>杨意不逢，抚凌云而自惜；钟期既遇，奏流水以何惭？</em>
      <p>自君之出矣，明镜暗不治。思君如流水，何有穷已时。</p>
      <b>心的春陌，有一朵是为我绽放，不求开的多么盛世；只愿让我感受：春，真的来过，我，没有被光阴遗落，便足矣。</b>
  </section>
  ```

  **伪类实现：**

  要修改第一个p元素的样式，我们可以通过伪元素 `:first-child` 实现：

  ```css
  .box_01 p:first-child {
      font-size: 30px;
      font-weight: bold;
      color: green;
  }
  ```

  [查看DEMO](https://1927344728.github.io/demo-lizh/html/13-伪元素和伪类.html?type=1)

  除此之外，我们可以直接给这个p元素加类来实现：

  ```css
  .first_child {
      font-size: 30px;
      font-weight: bold;
      color: red;
  }
  ```

  ```html
  <section class="box">
      <p class="first_child">江南可采莲，莲叶何田田。</p>
      ...
  </section>
  ```

  [查看DEMO](https://1927344728.github.io/demo-lizh/html/13-伪元素和伪类.html?type=2)

  **伪元素实现：**

  要修改第一行的首字的样式，我们可以通过伪元素 `::first-letter` 实现：

  ```css
  .box_03::first-letter {
      font-size: 30px;
      font-weight: bold;
      color: green;
  }
  ```

  [查看DEMO](https://1927344728.github.io/demo-lizh/html/13-伪元素和伪类.html?type=3)

  除此之外，要实现这个效果，就要为首字母创建一个元素，再添加样式：

  ```css
  .first_letter {
      font-size: 30px;
      font-weight: bold;
      color: red;
  }
  ```

  ```html
  <section class="box">
      <p><span class="first_letter">江</span>南可采莲，莲叶何田田。</p>
      ...
  </section>
  ```

  [查看DEMO](https://1927344728.github.io/demo-lizh/html/13-伪元素和伪类.html?type=4)

## 常见问题

### CSS3 新增伪类有那些？

上文常见伪类列表中大多都是 CSS3 新增。

CSS2 的伪类有 ：`:first-child`、`:link`、 `:visited`、`:hover`、`:active`、`:focus`、`lang`。

### CSS3 新增伪元素有那些？

上文常见伪元素列表中大多都是 CSS3 新增的。

CSS2 的元素有 ：`:first-line`、`:first-letter`、`:before`、`:after`。

## 参考链接

[MDN 伪类和伪元素](https://developer.mozilla.org/zh-CN/docs/Learn/CSS/Building_blocks/Selectors/Pseudo-classes_and_pseudo-elements)

[CSS :not伪类可能错误的认识](https://www.zhangxinxu.com/wordpress/2019/07/css-not-pseudo-class/)
