likes
comments
collection
share

1.6W+字揭秘69个有趣的CSS伪类

作者站长头像
站长
· 阅读数 9
1.6W+字揭秘69个有趣的CSS伪类

CSS伪类(Pseudo-class)是一种用于选择HTML元素的特殊方式,可以根据元素的状态或位置来应用样式,而不仅仅是基于元素的名称或属性。伪类在CSS中以冒号(:)开头,并且通常用于选择用户交互行为的元素或元素的特定状态。

格式([]表可选):

[element]:Pseudo-class[(argument)] {...}

目前在MDN中,列出了9种类型共69个伪类(不包括:-moz-前缀的伪类),其中有16个伪类在大部分浏览器中都不兼容,甚至有还未有浏览器兼容的伪类。在本文当中,这16个伪类的标题将用(❗)进行标志。接下来,本文将根据MDN分类别介绍每一个伪类。

元素显示状态伪类

该部分伪类允许根据元素的显示状态选择元素。

:fullscreen

由于MDN对该伪类的描述有点奇怪,这里我们以Fullscreen API标准的描述为主:

1.6W+字揭秘69个有趣的CSS伪类

:fullscreen伪类匹配满足下面两个条件任意一个的元素:

  • 元素的全屏标志已设置。也就是说元素处于全屏状态。

  • 元素是一个Shadow host,并且将该元素所属的节点文档(通常是整个页面的文档)的全屏元素(即当前全屏元素)重定向到这个Shadow host元素。那么这个Shadow host元素就满足:fullscreen伪类的条件。

    其中标准对全屏元素的描述是:所有文档都有一个关联的全屏元素。全屏元素是文档顶层中设置了全屏标志(如果有的话)的最顶层元素,否则为null。进入到全屏模式的元素在控制台会显示(需要开启显示用户代理Shadow DOM):

    1.6W+字揭秘69个有趣的CSS伪类

注意:

  • 该伪类不会匹配浏览器自带的缩放功能的全屏效果。

  • 元素处于全屏模式下时,部分属性不会生效,比如widthheight等等。

🌰:

<head>
  <style>
    .fullscreen {
      width: 100px;
      height: 100px;
      background-color: lightblue;
    }

    span {
      background-color: lightgreen;
    }

    :fullscreen {
      background-color: lightpink;
    }
  </style>
</head>

<body>
  <div class="fullscreen">
    <!-- 子代不受影响 -->
    <span>test</span>
  </div>
  <button class="toggleFullScreen">切换全屏</button>
  
  <script>
    const toggleFullScreen = document.querySelector('.toggleFullScreen')
    const fullscreen = document.querySelector('.fullscreen')

    toggleFullScreen.addEventListener('click', () => {
      fullscreen.requestFullscreen()
    })
  </script>
</body>

:modal

:modal表示一个元素处于一种排除与外部元素的所有交互状态,直到这种状态被解除。包括:

  • 通过element.showModal()方法打开的模态框(通过<dialog>open属性打开的非模态框不受影响)。也可以直接在CSS中使用dialog {...}设置模态框和非模态框的样式。

  • 通过requestFullscreen()方法让元素进入全屏模式的元素。没错,将上面那个🌰的:fullscreen改成:modal也会让.fullscreen元素的背景变成浅粉色.

    Selectors Level 4(以下简称SL4)规范对此的说明是:因为这阻止了与页面其余部分的交互。

注意:该伪类的兼容性一般,Chrome浏览器需要 105及以上的版本才支持。

:picture-in-picture

:picture-in-picture匹配当前处于画中画模式的元素。注意匹配的是进入画中画后留在原地的那个元素,而不是那个小视频弹窗。

1.6W+字揭秘69个有趣的CSS伪类

注意::picture-in-picture的兼容性比:modal还差一点。

话不多说,直接上🌰:

<head>
  <style>
    :picture-in-picture {
      box-shadow: 0 0 0 5px red;
    }
  </style>
</head>

<body>
  <video src="xxx.mp4" controls class="myVideo"></video>
  <button class="pipBtn">进入画中画模式</button>

  <script>
    const myVideo = document.querySelector('.myVideo');
    const pipBtn = document.querySelector('.pipBtn');

    // 检查浏览器是否支持PIP
    if ('pictureInPictureEnabled' in document) {
      pipBtn.addEventListener('click', () => {
        if (document.pictureInPictureElement) {
          // 如果已经在PIP模式下,则退出PIP
          document.exitPictureInPicture()
        } else {
          // 启用PIP
          myVideo.requestPictureInPicture()
        }
      });
    } else {
      console.log('浏览器不支持PIP API');
    }
  </script>
</body>

输入状态伪类

这些伪类与表单元素相关,尤其是<input>,用于根据HTML属性和字段在交互前后的状态来选择元素。

:autofill

:autofill伪类选匹配用户代理自动填充的<input>元素,包括通过autocomplete属性或者浏览器的自动填充的元素。

注意:

  • 在自动填充之后,如果用户编辑了元素,那么该伪类将不会匹配该元素。
  • 部分浏览器的内部样式表中的:-webkit-autofill伪类当中会使用!important,使得我们不能在自己的规则集当中使用某些属性。比如Chrome浏览器就有:
    :-webkit-autofill {
      background-color: rgb(232, 240, 254) !important;
      background-image: none !important;
      color: -internal-light-dark(black, white) !important;
    }
    

🌰:

<head>
  <style>
    input:autofill {
      border: 3px solid lightblue;
    }
  </style>
</head>

<body>
  <form>
    <label for="email">邮箱</label>
    <input type="email" id="email" autocomplete="email" />
  </form>
</body>

:enabled:disabled

:enabled:disabled伪类分别匹配任何实际未禁用的<button><input><select><textarea><optgroup>元素或与表单相关的<option>元素;和处于禁用状态的任何元素

当前是否处于禁用/未禁用状态主要看disabled属性,有些元素不具备disabled属性,即使使用了disabled属性,:disabled伪类也不会匹配,:enabled伪类也同理。

另外还有一个也可以使元素禁止交互的属性 -- inert:disabled伪类不会匹配它,inert是让用户代理忽略该元素交互,并没有禁用,但跟禁用不远了。

注意:有些CSS属性可能会影响用户与给定表单元素交互的能力,但不影响:enabled:disabled的匹配,比如displayvisibility属性对:enabled:disabled没有影响。

🌰:

<head>
  <style>
    :enabled {
      background-color: lightblue;
    }
    
    :disabled {
      background-color: lightpink;
    }
  </style>
</head>

<body>
  <form>
    <button>提交</button>
    <button disabled>重置</button>
  </form>
</body>

:read-write:read-only

:read-write伪类匹配满足以下任意条件之一的元素:

  • 没有指定readonlydisabled属性的<input><textarea>元素。
  • 既不是<input>元素,也不是<textarea>元素,但可编辑的元素(比如应用了contenteditable属性的元素)。

:read-only则匹配排除上述条件的其他所有HTML元素(包括<html><body>),这些元素被认为是只读的,不能由用户修改。

🌰:

<head>
  <style>
    :read-only {
      background-color: lightpink;
    }

    :read-write {
      background-color: lightblue;
    }
  </style>
</head>

<!-- <body>也被:read-only匹配 -->
<body>
  <input type="text" />      <!-- :read-write匹配 -->
  <textarea cols="30" rows="10"></textarea>   <!-- :read-write匹配 -->
  <p contenteditable>段落</p>    <!-- :read-write匹配 -->
  <p>段落</p>     <!-- :read-only匹配 -->
</body>

:placeholder-shown

:placeholder-shown伪类匹配显示占位符文本(placeholder属性)的<input><textarea>元素。与:autofill伪类一样,当用户编辑了元素,占位符消失之后,:placeholder-shown伪类不再匹配该元素。

:default

:default伪类匹配满足以下任意条件之一的元素:

  • 提交按钮(即<button type="submit">),这是其表单所有者的默认按钮。同时也适用于<input type="submit"><input type="image">
  • 初始checked属性为true,且类型checkbox或者radio<input>元素。
  • 初始checked属性为true,或者是按顺序排列为第一个的<option>元素。

:checked

:checked伪类匹配满足以下任意条件之一的元素:

  • 类型为checkbox或者radio,并且checked属性为true<input>元素。
  • checked属性为true,或者是按顺序排列为第一个的<option>元素。

:checked:default伪类的区别除了:checked不匹配submit按钮之外,:checked会随着用户选中其它元素而跟着改变,而:default只会匹配初始选中的那一个元素,后续即使用户选择了其它元素,checked属性变为了false,它也不会跟着变。

:indeterminate

:indeterminate伪类匹配满足以下任意条件之一的元素:

  • 类型为Checkbox,且通过Javascript将indeterminate属性设置为true<input>元素。
  • 类型为radio,且整个单选按钮组都没有checked属性为true<input>元素。
  • 没有设置value属性的<progress>元素。

其中indeterminate属性在MDN没有相应的词条内容,但是在<input type="checkbox">指南里,有对该属性的介绍:

除了选中和未选中状态之外,复选框还可以处于第三种状态:不确定。在这种状态下,无法说出项目是打开还是关闭。这是通过JavaScript使用HTMLInputElement对象的indeterminate属性设置的(不能使用HTML属性设置):

inputInstance.indeterminate = true;

更多详情请查阅<input type="checkbox">

另外在HTML标准当中,也有这样的描述:

IDL indeterminate属性最初必须设置为false。获取时,它必须返回它设置为的最后一个值。设置时,必须将其设置为新值。除了更改复选框控件的外观外,它没有任何效果

🌰:

<head>
  <style>
    input:indeterminate {
      box-shadow: 0 0 0 3px lime;
    }
  </style>
</head>

<body>
  <form>
    <fieldset>
      <legend>食谱</legend>

      <input type="checkbox" id="lemonade">
      <label for="lemonade">常温柠檬水</label>

      <ul>
        <li>
          <input type="checkbox" id="lemon">
          <label for="lemon">柠檬若干片</label>
        </li>
        <li>
          <input type="checkbox" id="syrup">
          <label for="syrup">糖浆适量</label>
        </li>
        <li>
          <input type="checkbox" id="water">
          <label for="water">水适量</label>
        </li>
      </ul>
    </fieldset>
  </form>

  <script>
    const lemonade = document.querySelector('#lemonade')
    const inputNodeList = document.querySelectorAll('ul input')
    const inputList = Array.from(inputNodeList)

    let checkedCount = [] 
    function isCheckedInput (e) {
      checkedCount = inputList.map(item => item.checked).filter(item => item)
      
      /*
       * 如果全部input元素都被选中,则让#lemonade进入被选中状态
       * 当被选中的input元素的数量大于0小于大于inputList.length时,让#lemonade进入被不确定状态
       * 否则让#lemonade进入被未选中状态
       */
      if (checkedCount.length === inputList.length) {
        lemonade.indeterminate = false
        lemonade.checked = true
      } else if(checkedCount.length > 0) {
        lemonade.indeterminate = true
        lemonade.checked = false
      } else {
        lemonade.indeterminate = false
        lemonade.checked = false
      }
    }

    for (let i = 0; i < inputList.length; i++) {
      inputList[i].addEventListener("click", isCheckedInput);
    }
  </script>
</body>

:blank(❗)

1.6W+字揭秘69个有趣的CSS伪类

:blank伪类匹配输入值为空的用户表单元素(由空字符串或null输入组成)。SL4对:blank伪类描述是:

1.6W+字揭秘69个有趣的CSS伪类

即解释表单控件上:blank的经验法则是:

  • 如果控件始终会提交,并且使用空字符串执行此操作,则它与:blank匹配(例如当<input type="text">值为空时)。
  • 如果它有时提交,并被设置为不提交,它就匹配:blank(例如未选中的<input type="checkbox">)。
  • 如果是“操作按钮”(而不是表示状态的“切换按钮”),例如<button><input type=submit>等,则它永远不会匹配:blank

注意:该伪类是有风险的

:valid:invalid

:valid:invalid伪类分别匹配其内容验证成功和验证失败的表单元素。

约束验证的规则可以通过如requiredpatternminmax等等属性来定义,当用户提交包含这些元素的表单时,浏览器会检查它们的输入是否符合这些规则,并相应地标记这些元素为有效(valid)或无效(invalid)。

目前这两个伪类的解释还是很简单的,但是在HTML标准中对它们的匹配描述很饶,很难理解:

1.6W+字揭秘69个有趣的CSS伪类

:valid为例,这第一条还好理解,就是匹配满足验证条件的元素。但是第二和第三条就很绕:将其翻译一下就是:

1.6W+字揭秘69个有趣的CSS伪类

1.6W+字揭秘69个有趣的CSS伪类

反正我是不理解,要是理解的大佬麻烦戳我一下,salute~ 目前就全当这两个伪类匹配满不满足验证条件的元素。

另外,在Sl4中描述道:

没有约束的元素和根本没有数据有效性语义的元素是有区别的,前者没有约束,因此总是有效的,后者既不是有效的,也不是无效的。例如,在HTML中,<input type="text">元素可能没有约束,但<p>元素根本没有有效性语义,因此它永远不会被这两个伪类中的任何一个匹配。

如描述所言,有些表单元素比如<fildset>,它没有什么约束验证的规则,但它始终是有效的,即使其后代有不满足验证条件的元素,它还是有效的。

:user-valid:user-invalid(❗)

1.6W+字揭秘69个有趣的CSS伪类

:user-valid:user-invalid:valid:invalid类似,但前者仅在用户与之进行了重要交互之前和之后匹配。

根据SL4的描述是:

  • 在用户尝试提交表单到再次与表单元素交互之前的这段时间内,:user-invalid伪类匹配:invalid:out- range或输入值为空但是:required能匹配的元素。
  • 在用户尝试提交表单到用户再次与表单元素交互之前,user-valid伪类匹配:valid能匹配的元素。

用户代理可能允许它们在其他时间匹配这些元素,因为这适合向用户突出显示错误。例如,一旦用户在其中输入了一些文本并将焦点更改为另一个元素,UA可能会选择使用:user-invalid匹配:invalid能匹配的元素,并且只有在用户成功更正输入后才停止匹配。

1.6W+字揭秘69个有趣的CSS伪类

:in-range:out-of-range

:in-range:out-of-range伪类匹配具有范围限制(范围由minmax属性指定),且当前值在范围内和在范围外的元素。

注意:

  • minmax属性的值的具体内容通常是看其应用的元素,比如如果应用到<input type="number">,那值就是数字;如果是<input type="date">,那值就是日期。可以简单理解只要能应用minmax的元素,就是具有范围限制的元素,满足条件就可以被这两个伪类匹配。
  • <input type="password">可以使用minlengthmaxlength属性,但是这两个伪类对它们没有反应。但是它们是验证约束规则。

:required:optional

这两个伪类很简单,:required:optional伪类分别匹配requiredtrue(即必需项)和false(即可选项)的<input><select><textarea>元素。

注意:非表单元素的元素既不是必需的,也不是可选的。

语言伪类

这些伪类反映文档语言,用于根据语言或脚本方向选择元素。

:dir(ltr)/:dir(rtl)(❗)

1.6W+字揭秘69个有趣的CSS伪类

:dir()伪类用于根据文档语言确定的方向来匹配元素。:dir(ltr)匹配文档语言方向性为从左到右的元素,:dir(rtl)则相反。

dir()的参数必须是单个标识符,否则选择器无效。参数是ltrrtl以外的值不会报错,但是不匹配任何东西。

注意::dir()伪类不受样式影响,比如direction属性不会影响它的匹配。

SL4::dir(C)[dir=C]之间的区别在于:[dir=C]只对元素上的给定属性执行比较,而:dir(C)伪类使用UA对文档语义的了解来执行比较。例如,在HTML中,元素的方向性是继承的,因此没有dir属性的子元素将具有与其最近的具有有效dir属性的祖先元素相同的方向性。另一个例子是,在HTML中,匹配[dir=auto]的元素将匹配:dir(ltr):dir(rtl),这取决于元素的解析方向(由其内容决定)。

:lang()

:lang()伪类根据元素语言来匹配页面元素。其参数遵循BCP47语法,并且可以以逗号分隔指定多种语言(即:lang(A, b));如果需要匹配相同的地区子标签,可以使用通配符后接地区子标签的形式提供参数:lang(*-Latn)

通配符语言范围("*")不匹配未设置lang属性或者lang=""的元素(<html>可能没有设置lang的情况下),但匹配lang="und"(即未确定)的元素。:lang("")匹配只匹配没有设置lang属性的元素。匹配不区分ASCII大小写。语言范围不需要是有效的语言代码来执行此比较。

但是要注意,大部分浏览器不兼容语言列表和通配符的参数格式。 1.6W+字揭秘69个有趣的CSS伪类

🌰:

<head>
  <style>
    *:lang(zh-CN) {
      color: red;
    }
  </style>
</head>

<body>
  <p lang="zh-CN">段落</p>
  <p lang="en-us">It's a long <span lang="zh-CN">段落</span></p>
</body>

位置伪类

这些伪类与链接以及当前文档中的目标元素相关。

link:visited

link:visited伪类分别匹配还未访问过和已经访问过的超链接元素。两个伪类相斥。

注意:在经过一段时间之后,用户代理可能会选择将访问过的链接返回到:link(未访问的)状态。

SL4:由于样式表作者可能会滥用:link:visited伪类来确定用户在未经用户同意的情况下访问了哪些网站,因此UA可能会将所有链接视为未访问的链接,或实施其他措施来保护用户的隐私,同时以不同的方式呈现已访问和未访问的链接。

:any-link

:any-link匹配任意超链接源锚元素。它的匹配机制是只要指定了href属性(即使值为空)并且属性有效就匹配,比如带有href<a><area>等等元素。

SL4:如果元素匹配:link:visited,那么:any-link也匹配该元素,等价于:is(:link,:visited)

注意::any-link不匹配<link>元素。

🌰:

<head>
  <style>
    :any-link {
      color: lightpink;
    }
  </style>
</head>

<body>
  <a>连接</a>      <!-- 不匹配 -->
  <a href="https://www.baidu.com/">连接</a>     <!-- 匹配 -->
</body>

:local-link(❗❗)

1.6W+字揭秘69个有趣的CSS伪类

:local-link匹配指向同一文档的超链接元素,它的匹配机制是元素href属性的绝对URL匹配该元素所在文档的URL。如果超链接的href属性包含一个片段URL,那么当前URL的片段URL也必须匹配;如果没有,则在比较中不考虑当前URL的片段URL部分。

SL4:当前页面的URL可能会因用户操作而改变,例如在同一页面中激活针对不同片段的链接;或者通过使用pushState API;以及更明显的操作,如导航到另一个页面或遵循重定向(可以由HTTP等协议发起),标记指令,如<meta HTTP -equiv="…>,或编写脚本指令)。UA必须确保:local-link正确响应状态中的所有此类更改。

🌰:

<head>
  <style>
    :local-link {
      color: lightpink;
    }
  </style>
</head>

<body>
  <a href="#xxx">同文档下的锚点链接</a>    <!-- 匹配 -->
  <a href="https://www.baidu.com/">外部文档链接</a><br /> <!-- 不匹配 -->
</body>

:target

有时候我们在浏览一些网页的时候,比如浏览百度百科:

1.6W+字揭秘69个有趣的CSS伪类

当点击右侧栏中的选项时,页面会跳转到该选项所指向的那个元素。通常我们的实现方法是:

<!-- 点击链接跳转到当前页面下id为target的元素 -->
<a href="#target">target</a>
<!-- ... -->
<div id="target"></div>

此时这个idtarget的元素就是文档的目标元素。而:target伪类就匹配目标元素。

注意::target不能在Web Component中工作,因为Shadow root没有将目标元素向下传递到Shadow Tree。

:target-within(❗)

1.6W+字揭秘69个有趣的CSS伪类

:target-within伪类与:target类似,后者能做到的,前者都能做到,但是前者还适用于包含在:target元素内部的任何节点,包括Web Component。简单地说,就是:target-within匹配目标元素本身和其后代元素。

🌰:

<head>
  <style>
    div {
      padding: 10px;
    }

    p {
      background-color: lightpink;
    }

    :target {
      background-color: lightblue;
    }
  </style>
</head>

<body>
  <a href="#target">连接</a>
  
  <!-- div和p都匹配 -->
  <div id="target">
    <p>段落</p>
  </div>
</body>

:scope

通常我们在CSS中选择元素时,选择器会按照规则在整个文档中查找匹配的元素。但如果只想在特定作用域内查找元素时,就可以使用:scope伪类来实现。

目前在CSS中使用:scope伪类等同于:root,MDN的说明是:因为目前尚无一种方法来显式建立作用域元素。通常该伪类是在Javascript的DOM API中使用,来匹配调用DOM API方法的元素。

比如来看MDN的🌰:

<body>
  <p id="para">
  This is a paragraph. It is not an interesting paragraph. Sorry about that.
  </p>
  <p id="output"></p>

  <script>
    const paragraph = document.getElementById("para");
    const output = document.getElementById("output");

    // 检查变量paragraph是否与给定的选择器:scope匹配。
    if (paragraph.matches(":scope")) {
    output.textContent = "The first paragraph is its own scope, as   expected!";
    }
  </script>
</body>

其中的:scope指的是使用getElementById方法获取的具有特定id(即para)的元素本身。

资源状态伪类

这些伪类适用于能够处于被描述为播放和暂停状态的多媒体元素。

:playing:paused:seeking:buffering:stalled:muted:volume-locked(❗)

这7个葫芦娃的兼容性一致,以:playing为例: 1.6W+字揭秘69个有趣的CSS伪类

以下是他们的定义:

  • :playing伪类:匹配可以被播放或暂停的元素,并且该元素正在播放中。这包括了元素被显示播放,以及当由于某些原因(与用户意图无关)而临时停止时,会自动恢复播放的情况,例如出现缓冲或卡住的状态。

  • :paused伪类:匹配可以被播放或暂停的元素,并且该元素处于暂停状态时。这包括了显示的暂停状态,以及其他不在播放状态的情况(如已加载但尚未激活)等。

  • :seeking伪类:匹配可以进行seek(即在音频或视频播放中寻找或跳转到特定的时间点或位置)操作的元素,当这个元素正在进行seek操作时。

  • :buffering伪类:匹配正在尝试获取媒体数据,但还没有足够的数据来继续播放(也就是缓冲状态)的元素。如果使用:buffering匹配一个元素,同时也会匹配:playing,表示该元素仍然被视为在播放状态,尽管它正在等待数据加载。

  • :stalled伪类:匹配由于某种原因无法继续播放,因为它正在尝试获取媒体数据但已经一段时间没有成功获取到数据的元素。对于HTML中的音频和视频元素,这个时间段由媒体元素的Stall Timeout(指媒体数据加载超时时间)来决定。

    :buffering类似,:stalled匹配一个元素时,也会匹配:playing,表示该元素仍然被视为在播放状态,尽管它因数据加载问题而暂停。

  • :muted伪类:匹配能够发出声音,但是当前被设置成了静音(强制静音)时的元素。

  • :volume-locked伪类:匹配能够发出声音,但是当前其音量被用户代理或用户锁定,导致无法更改音量时的元素。

时间维度伪类

这些伪类根据某些时间轴中当前显示或活动位置对元素进行分类,例如在文档的语音呈现期间,或者在使用WebVTT呈现字幕的视频显示期间。

:current:past:future(❗)

  • :current伪类:匹配当前正在显示的元素或元素,或者包含当前显示元素的祖先元素。它还有一个:current()备用形式,接受一个复合选择器列表作为参数,表示与参数匹配的当前元素,如果当前元素不匹配参数,那么选择最接近当前元素且匹配参数的祖先元素。如果当前元素及其祖先都不匹配参数,那么选择器不会匹配任何元素。
  • :past伪类:匹配在文档中位于:current元素之前的所有元素。如果文档没有明确定义元素的时间顺序,那么:past将选择当前元素之前的所有元素,即当前元素的前一个兄弟元素(如果有的话)。
  • :future伪类:与:past相反,匹配在文档中位于当前元素之后的所有元素。

这些伪类通常与Web VTT一起使用,比如像MDN的🌰:

<head>
  <style>
    :current(p, span) {
      background-color: yellow;
    }
  </style>
</head>

<body>
  <video controls preload="metadata">
  <source src="video.mp4" type="video/mp4" />
  <source src="video.webm" type="video/webm" />
  <track
    label="English"
    kind="subtitles"
    srclang="en"
    src="subtitles.vtt"
    default />
</video>
</body>
WEBVTT FILE

1
00:00:03.500 --> 00:00:05.000
This is the first caption

2
00:00:06.000 --> 00:00:09.000
This is the second caption

3
00:00:11.000 --> 00:00:19.000
This is the third caption

假设现在第二段字幕,那么:current匹配2这一段字幕,:past:future则分别匹配第1和第3段字幕,也就是匹配2之前和之后的所有元素。

树结构伪类

这些伪类根据元素在文档树中的位置或层次结构来匹配它们。

:root

:root伪类用于匹配文档的根元素。在DOM文档中,根元素通常是<html>元素,除非通过脚本修改了文档结构。一般用于设置根元素的字体大小,或者用于声明自定义CSS属性(变量)来在整个文档中定义全局的样式值

:empty

:empty伪类匹配没有子项(除非是可选的文档空白字符)的元素,以下几种情况将判断元素内容为空:

  • 标签内什么都没有,比如<div></div>
  • 标签内注释,比如<div><!-- a box --></div>
  • 标签内处理指令,处理指令是一种用于告诉解析器或处理器如何处理文档的特殊指令,以<?开始,以?>结束,比如<?xml version="1.0" encoding="UTF-8"?>。处理指令在HTML中很少会用到,当标签内有处理指令时也为空。
  • CSS content属性,比如div::after {content: 'box'}。这直接说明伪类不影响元素被判空。
  • 以上除第一条之外的多种组合。

这些情况会被判空的前提是没有标签内没有空格,SL4的说法是:

在SL2和3中,:empty伪类不会匹配只包含空格的元素。这样做的目的是,给定的空格在HTML中很大程度上是可折叠的,可以用于源代码格式化,特别是因为省略结束标记的元素很可能会在其DOM文本内容中吸收这样的空白 — 作者认为为空的元素可以由这个选择器选择,正如他们所期望的那样。

实际上只要元素包含空格、换行符和制表符等等,:empty伪类就不会匹配该元素。

🌰:

<head>
  <style>
    div { 
      width: 100px;
      height: 50px;
      background-color: lightblue;
      margin-bottom: 5px;
    }
    
    .test::after {
      content: 'box';
    }

    :empty {
      border: 3px solid red;
    }
  </style>
</head>

<body>
  <div></div>   <!-- 匹配 -->
  <div><?xml version="1.0" encoding="UTF-8"?></div>   <!-- 匹配 -->
  <div><!-- a box --></div>  <!-- 匹配 -->
  <div class="test"></div>   <!-- 匹配 -->
  <div><span></span></div>  <!-- div不匹配,span匹配 -->
  <div>&nbsp;</div>     <!-- 不匹配 -->
  <div> </div>     <!-- 不匹配 -->
</body>

MDN对:empty伪类的无障碍访问问题描述:屏幕阅读器等辅助技术无法解析空的交互式内容。所有交互式内容都必须有一个可访问的名称,该名称是通过为交互式控件的父元素(锚点、按钮等)提供文本值来创建的。可访问名称将交互式控件公开给可访问树,可访问树是一种传递对辅助技术有用信息的API。


提供交互式控件的可访问名称的文本可以使用组合属性隐藏,这些属性可以从屏幕上直观地删除它,但使其可被辅助技术解析。这通常用于仅依靠图标来传达目的的按钮。

子节点索引伪类

这些伪类用于根据父元素的子列表中的索引来匹配元素,包含让各位又爱又恨的伪类。

:nth-child():nth-last-child()

:nth-child()伪类根据子元素在父元素内所有同级元素中的位置来匹配子元素;:nth-last-child():nth-child()类似,但是从末尾开始计数。

其语法格式为:

:nth-child((Keyword | Functional notation) [of <complex-selector-list>]?) {}

其中(这同样适用于下面的:nth-of-type():nth-last-of-type(),认真学习哦~):

  • Keyword值有oddeven,分别表示在一系列同级中的数字位置为奇数偶数的元素。

  • Functional notation即An+BAn+BAn+B规则:确定符合条件的元素的位置,即在一系列同级中的第An+BAn+BAn+B个元素。其中:

    • nnn是任意非负整数。表示当前元素在选择器列表S中的索引。
    • AAA为整数步长。即每隔多少个元素开始重复一次选择。如果AAA的值为0,则表示没有周期,会匹配所有元素。
    • BBB为整数偏移量。即从哪个位置开始选择。B的值为0时,表示从第一个元素开始选择。

    比如:nth-child(2n+1)表示:

    (2×0)+1、(2×1)+1、(2×2)+1、...(2×0)+1、(2×1)+1、(2×2)+1、...(2×0)+1(2×1)+1(2×2)+1...

    即跟odd一样;如果包含负号,比如:nth-child(-n+3),它表示

    −0+3、−1+3、−2+3-0+3、-1+3、-2+30+31+32+3

    即匹配前三个元素。-表示倒数计数,在CSS中,子元素是按照它们在父元素中的顺序创建和定位的。正数和负数的使用表示了从前往后或从后往前选择的不同方向。当结果为负数时,会停止匹配。

    通过调整AAABBB的值,可以创建出各种序列,可以选择不同的元素位置。

  • of <complex-selector-list>简单的说就是一个选择器列表,拿MDN的🌰来说就是(这种写法在编辑器当中会警告,但是不影响最终效果):

    /* 匹配类名为important的前三个li元素 */
    :nth-child(-n + 3 of li.important) {}
    

    注意,这与下面这行代码不同:

    /* 匹配位于前三项并且类名为important的li */
    li.important:nth-child(-n + 3) {}
    

    前者是先看类名再选前三项;后者的首要条件是前三项,之后再看类名。用JS数组来说就相当于前者是array.filter().slice();后者是array.slice().filter() 1.6W+字揭秘69个有趣的CSS伪类

除此之外,:nth-child()还可以连续使用,比如:

/* 表示匹配第三个到第6个元素 */
li:nth-child(n+3):nth-child(-n+6)

:first-child:last-child:only-child

:first-child:last-child伪类分别匹配在其包含的兄弟元素中的第一个和最后一个元素。相当于:nth-child(1):nth-last-child(1)

:only-child伪类匹配没有兄弟元素的元素,即它是其父元素的唯一子元素(独生子女)。相当于:first-child:last-child:nth-child(1):nth-last-child(1),但权重较低。

类型化子节点索引伪类

与子节点索引伪类相似,但是其解析是基于一个元素在同一类型(标签名)的兄弟元素列表中的位置。

:nth-of-type():nth-last-of-type():first-of-type:last-of-type:only-of-type

与子节点索引伪类类似,但是这部分伪类只考虑相同元素类型的子元素。在某些情况下,子节点索引伪类和类型化子节点索引伪类能达到相同的效果,就像SL4对:nth-of-type()的描述:

如果预先知道元素的类型,则此伪类相当于使用:nth-child()和类型选择器一起使用。也就是说,img:nth-of-type(2)等价于*:nth-child(2 of img)

注意::only-of-type是匹配在其父元素中没有相同类型的兄弟元素的元素。它不要求元素是其父元素的唯一子元素。

用户操作伪类

这些伪类用于匹配用户执行特定操作时的元素状态。

:hover

:hover伪类在用户的指针设备悬停在元素或者伪元素上时匹配。像平板或手机等由于硬件限制而无法检测到悬停元素上的设备,仍然被认为是符合规范的交互式用户代理,通过笔触或者手指点击的方式仍然可以让元素被:hover伪类匹配。

正常情况下,后代元素都是在其祖先元素盒内,如果现在有:

div:hover {
  color: lightpink;
}

并且该<div>有后代节点(包括元素节点和非元素节点),那么当指针设备悬停在其后代节点上时,整个<div>包括其后代元素都会被:hover匹配,因为<div>元素的后代节点中至少有一个满足鼠标悬停条件。

:active

:active伪类在用户激活元素时匹配元素。以下情况元素被视为处于激活状态,能被:active匹配:

  • 用户按下鼠标或触摸元素,到松开鼠标或停止触摸元素期间。
  • 当使用键盘,通过Tab键使元素被聚焦,到焦点离开元素(松开Tab键不会使元素变成非激活状态)期间。
  • 如果元素是一个按钮,并且元素被通过Tab键聚焦时,或者被点击过之后,按下空格键到松开空格键期间。

:active伪类不限制元素类型,只要元素满足以上条件即可被匹配,不需要像聚焦状态那样,某些元素需要设置tabindex属性才能被聚焦(emmmm...至少在Chrome浏览器是这样,其它浏览器没测试过)。

MDN:

注意:在具有多按钮鼠标的系统上,CSS指定:active伪类必须仅适用于主按钮;在右手鼠标上,这通常是最左边的按钮。

:focus:focus-visible:focus-within

  • :focus匹配通过Tab键、鼠标点击、触摸等形式获得焦点的元素或伪元素。
  • :focus-visible伪类只在对用户最有帮助的情况下才可见地指示焦点,而不是始终可见,并且可以用于指定默认焦点样式。

    SL4:以下(非规范)建议可以作为何时指示当前聚焦元素的起点:

    • 如果用户已表达了偏好(例如通过系统偏好设置或浏览器设置)始终看到焦点指示器,则无论其他因素如何,都指示焦点(另一种选择可能是,无论作者样式如何,用户代理都显示自己的焦点指示器)。
    • 如果元素支持键盘输入(例如<input>元素,或任何在焦点时会触发虚拟键盘显示的元素,如果没有物理键盘存在),则指示焦点。
    • 如果用户通过键盘或其他非指针设备与页面交互,则指示焦点。(这意味着使用键盘可能会改变此伪类的匹配,即使它不影响:focus。)
    • 如果用户通过指针设备(鼠标、触摸屏等)与页面交互,并且焦点元素不支持键盘输入,则不指示焦点。
    • 如果先前焦点的元素指示了焦点,并且脚本导致焦点发生变化,则在新焦点元素上指示焦点。相反,如果先前焦点的元素未指示焦点,并且脚本导致焦点移动到其他地方,则不要在新焦点元素上指示焦点。
    • 如果新显示的元素自动获得焦点(例如在新打开的对话框中的操作按钮),则应该指示焦点。
  • focus-within伪类类似于:focus,可以应用于:focus伪类所应用的任何元素或伪元素,同时它还类似target-within,适用于包含在:focus元素内部的任何节点,包括Web Component。

通常是表单、链接等类型的元素,即可导航元素,可以通过键盘导航访问;而非导航元素(比如<div>)默认不可聚焦,不会被:focus匹配。

可以通过在非导航元素上使用tabindex属性使元素变成可聚焦,从而可以被:focus匹配。

<head>
  <style>
    /* my-counter::part(test):hover {
     *   color: red;
     * }
     */
     
    :focus {
      color: lightpink;
    }
  </style>
</head>

<body>
  <script>
    class Counter extends HTMLElement {
      count = 0

      connectedCallback() {
        const shadow = this.attachShadow({mode: 'open'});
        
        setInterval(() => {
          /* shadow.innerHTML = `
           *   <span part='countspan'>${this.count++}</span>
          `* //this.innerHTML的值每秒加一
           */
          shadow.innerHTML = `
            <span tabindex='1'>${this.count++}</span>
          ` //this.innerHTML的值每秒加一
        }, 1000)
      }
    }
    
    // 注册元素
    customElements.define('my-counter', Counter)
  </script>
  
  <!-- 使用元素 -->
  <my-counter></my-counter>
</body>

可以直接匹配Shadow DOM,而其它部分伪类,比如:hover则需要通过其它方式(比如part属性)才会有效果,学习成本相对要高一点。

:focus伪类与:link:visited:hover:active存在匹配顺序 -- LVHFA顺序,即它们之间的匹配顺序是::link:visited:hover:focus:active。这个顺序只在具备href属性且有效的元素上,因为:link:visited不匹配其它元素。

可以将这个顺序看出两条线:

  • :link:hover:focus:active
  • :visited:hover:focus:active(访问链接后)。

因为:link:visited互斥,只要访问链接后就会一直是:visited,除非与页面失去连接之后再次进入页面。在MDN的样式化链接中可以进行自定义测试。

函数伪类

这些伪类接受选择器列表或容错选择器列表作为参数。

:is():where()

:is()伪类接受选择器列表作为参数,将多个选择器组合在一起,以便在样式规则中应用相同的样式,简化CSS代码并提高代码的可读性。

:where()伪类的语法和功能与:is()相同。但是:where()伪类及其任何参数都不会影响选择器的权重(始终为零)。如果一定要使用:is(),但又不想考虑权重等问题,可以将:is()作为参数传递到:where(),使得选择器的权重始终为0。

比如有这么一段代码:

ul li,
ol li, {
  color: lightblue;
}

div.test1, div#test2 {
  background: lightpink;
}

改用:is()来写就是:

:is(ul, ol) li {
  color: lightblue;
}

div:is(.test1, #test2) {
  background: lightpink;
}

效果是一样的。

但是要注意,它会计算出所有选择器组合,可能会产生类似组合爆炸等意料之外的效果,比如改动一下上面的部分代码:

/* 虽然这很少见,但不乏有人想贪小便宜将类似的元素都写一起 */
:is(ul, ol, dl) li, dt {
  color: lightblue;
}

/*
 * 这会产生以下几种组合:
 * ul li
 * ul dt
 * ol li
 * ol dt
 * dl li
 * dl dt
 */

另外要注意:

  • 如果解析后的参数列表为空,则:is()有效但不与任何元素匹配。
  • :is()伪类无法匹配伪元素。

:not()

:not()伪类与:is()的作用相反,它匹配指定选择器参数之外的元素。

通常在使用:not()时会写上前缀(暂且就这么叫吧...),即类似div:not(...)来缩小匹配范围,不然它会匹配到<html><body>,导致出现意料之外的问题:

/* 这会匹配<html>和<body>,导致.test1和.test4又继承它们的color,白排除了 */
:not(.test1 .test4) {
  color: lightblue;
}

还可以用:not()伪类来写不匹配任何元素的效果。比如::not(*|*),虽然这没什么用,但具有更高的权重。

在选择器的优先级计算中,:not()伪类的参数不会增加特定选择器的权重,由参数中最具体的选择器的权重决定。而 :is()伪类的参数会增加特定选择器的权重。

注意:

  • 如果参数中任意一个选择器无效,则整个规则也无效,因此相比:is():not()的容错率较低。
  • :not(.test1, .test2) 等同于:not(.test1):not(.test2),以此来同时排除多个选择器。

:has()

:has()伪类如果参数是正常的选择器或者选择器列表,那么它表示匹配包含匹配参数的子元素的父元素。比如:

div:has(p) {
  background-color: yellow;
}

它的意思是匹配包含<p>元素的<div>元素。

它还可以可以接受一个相对选择器:表示相对于一个或多个锚元素的元素的选择器,前面有一个组合器。如果相对选择器不以显式组合器开头,就具有隐含的后代组合器。

其实就是将那些选择器类型的符号(比如+)写在最前面,比如MDN的这段代码:

/* 
 * 表示选择div元素的直接相邻兄弟元素中具有id为reference的元素。
 * 这里的+是一个表示兄弟关系的组合器。 
 */
+ div#topic > #reference {}

/* 
 * 表示选择具有.icon类直接子元素的元素。
 * 这里的>是一个表示父子关系的组合器。
 */
> .icon {}

将它们作为参数传递给:has()就是:

/* 
 * 表示如果某个元素的相邻兄弟元素是一个id为"topic"的div元素,
 * 并且这个div元素内部包含一个id为"reference"的直接子元素,则应用该样式。
 */
:has(+ div#topic > #reference) {}

/* 表示选择包含具有class为"icon"的直接子元素的元素。 */4
:has(> .icon ) {}

类似的🌰还有很多,熟练使用:has()可以像使用正则表达式那样匹配元素。

注意:

  • :has()伪类不能作为参数嵌套在另一个:has()中。
  • :has():is()一样,会增加选择器的权重。

其它伪类

:defined

:defined用于匹配已定义的元素,这包括内置的标准元素和使用Web Component创建的自定义元素。

:host:host():host-context()

这三个伪类都用于匹配Web Component相关的元素。

:host伪类用于匹配Shadow DOM的Shadow Host。该伪类Shadow DOM外使用没有作用,通常是在Javascript中生成一个<style>,并塞到Shadow DOM中(这点适用于:host():host-context())。

比如改动:focus-within的🌰:

<head>
  <style>
    /* 达咩 */
    /* my-counter:host {
      color: lightpink;
    } */
  </style>
</head>

<body>
  <script>
    class Counter extends HTMLElement {
      count = 0

      connectedCallback() {
        const shadow = this.attachShadow({mode: 'open'});
        const style = document.createElement("style");
        const span = document.createElement("span");

        style.textContent = `
          :host {
            color: lightpink;
          }
        `
        setInterval(() => {
          span.innerText = this.count++
        }, 1000)

        shadow.appendChild(style) // 生效,宾勾~
        shadow.appendChild(span)
      }
    }
    
    // 注册元素
    customElements.define('my-counter', Counter)
  </script>
  
  <!-- 使用元素 -->
  <my-counter></my-counter>
</body>

1.6W+字揭秘69个有趣的CSS伪类

当在页面上多次使用<my-counter>,并且只希望设置其中某一个<my-counter>的样式时,可以使用将选择器列表作为参数传递给:host()伪类:

// ...
style.textContent = `
  :host(.test2) {
    color: lightpink;
  }`
    
// ...`
<my-counter class="test1"></my-counter>  // 不匹配
<my-counter class="test2"></my-counter>  // 匹配

:host-context()伪类选择自定义元素的父级元素,并根据父级元素的位置或属性来应用不同的样式。 1.6W+字揭秘69个有趣的CSS伪类

// ...
style.textContent = `
  :host-context(.div1) {
    color: lightpink;
  }`
    
// ...`
<div class="div1">
  <my-counter class="test1"></my-counter> // 匹配
</div>
<div class="div2">
  <my-counter class="test1"></my-counter> // 不匹配
</div>

:first:left:right

这三个伪类与@page规则(用于修改打印页面的不同方面。定位并修改页面的尺寸、方向和页边距。可用于使用其各种伪类来定位打印输出中的所有页面或子集。)一起使用,分别用于:

  • :first匹配打印文档的第一页。
  • :left匹配打印文档的所有左侧页面。
  • :left匹配打印文档的所有右侧页面。

在双面打印的情况下,每个物理纸张上会有左右两个页面。左页一般位于奇数页,而右页位于偶数页。

使用方式(以:first为例):

@page :first {
  margin-left: 20px;
  margin-top: 20px;
}

1.6W+字揭秘69个有趣的CSS伪类

:popover-open

:popover-open伪类匹配处于显示状态的弹出框元素(即具有popover属性的元素)。用于仅在显示弹出框元素时应用样式。

popover属性用于将元素指定为弹出框元素。它有相应的Web Popover API(用于在其他页面内容之上显示弹出框内容)。具体的内容之后在Web APIs专栏中再详细介绍(如果写的话...),这里只简单说明一下。

默认情况下,应用了popover属性的元素的displaynone,直到点击指定了popovertarget属性的元素或者调用API方法时才会显示弹出框元素。这与<dialog>类似。

🌰:

:popover-open {
  width: 200px;
  height: 100px;
  position: absolute;
  inset: unset;
  bottom: 5px;
  right: 5px;
  margin: 0;
}

参考资料

转载自:https://juejin.cn/post/7278512641781973027
评论
请登录