《重学前端》笔记--CSS 选择器

245 阅读11分钟

本文为winter发布在极客时间的《重学前端》的学习笔记。

大家支持正版喔:time.geekbang.org/column/arti…

导语

选择器的基本意义是:根据一些特征,选中元素树上的一批元素。

选择器可以大致分为以下几类:

  • 简单选择器:针对某一特征判断是否选中元素。
  • 复合选择器:连续写在一起的简单选择器,针对元素自身特征选择单个元素,表示“且”的关系。
  • 复杂选择器:由“(空格)”“ >”“ ~”“ +”“ ||”等符号连接的复合选择器,根据父元素或者前序元素检查单个元素。
  • 选择器列表:由逗号分隔的复杂选择器,表示“或”的关系。

简单选择器

前四个都好理解,这里简单记录下属性选择器和伪类选择器。

属性选择器

  • [att]:匹配具有某属性的标签,例如[title]表示所有具有title属性的标签
  • [att=val]:精确匹配,匹配属性中为某个值的标签,例如[href="foo"]表示所有具体href属性,且属性值为foo的标签
  • [att~=val]: 多种匹配,检查一个元素的值是否是若干值之一,这里的val不是一个单一的值了,可以是用空格分隔的一个序列。例如[title~="flower"]可以匹配<img src="/i/eg_tulip.jpg" title="tulip flower" />
  • [att|=val]:开头匹配,检查一个元素的值是否是以val开头,这里的值必须是完整且唯一的单词,或者以-分隔开,例如[title|="flower"]可以匹配<img src="/i/eg_tulip.jpg" title="flower tulip" />,但是不匹配<img src="/i/eg_tulip.jpg" title="tulip flower" />
  • [att*=val]:正则匹配,只要元素的值中存在val就行。例如[title*="flower"]可以匹配<img src="/i/eg_tulip.jpg" title="aaaaaflowerbbbbb" />
  • [att^=val]:正则匹配,只要元素的值以val开头就行。例如[title^="flower"]可以匹配<img src="/i/eg_tulip.jpg" title="flowerbbbbb" />
  • [att$=val]:正则匹配,只要元素的值以val结尾就行。例如[title$="flower"]可以匹配<img src="/i/eg_tulip.jpg" title="aaaaaflower" />

伪类选择器

伪类选择器是一系列由CSS规定好的选择器,它们以冒号开头。

伪类选择器有普通型和函数型两种,下面介绍主要使用的选择器,所有伪类选择器的汇总可以参考MDN

树结构关系伪类选择器

  • :root,伪类表示树的根元素。

对于 HTML 来说,:root表示 元素,除了优先级更高之外,与 html 选择器相同。但是随着scoped cssshadow root等场景出现,选择器可以针对某一子树来选择,这时候就很需要root伪类了。

声明全局CSS变量时,在:root中很有用:

:root {
  --main-color: hotpink;
  --pane-padding: 5px 42px;
}
  • :empty,伪类表示没有子节点的元素,子节点只可以是元素节点或文本(包括空格)。
// html
<div class="box"><!-- I will be lime --></div>
<div class="box"></div>
<div class="box">I will be pink</div>
<div class="box"><span><span></div>

// css
.box:empty {
    background: lime;
}

上面的例子中,.box:empty中仅前两个.box会生效。

  • :nth-child 和 :nth-last-child :nth-child(An+B)这个CSS伪类首先找到所有当前元素的兄弟元素,然后按照位置先后顺序从1开始排序,选择的结果为CSS伪类:nth-child括号中表达式(An+B)匹配到的元素集合(n=0,1,2,3...)。比如,3n+4匹配位置为 4、7、10、13...的元素。

此外,还可以使用:nth-child(odd)直接表示奇数位元素,等同于:nth-child(2n+1);可以用nth-child(2n)直接表示偶数位元素,等同于nth-child(2n)

:nth-last-child的区别仅仅是从后往前数。

  • :first-child 和 :last-child

也是查询所有当前元素的兄弟元素,但是分别表示第一个和最后一个元素。

  • :only-child

匹配没有任何兄弟元素的元素

  • :nth-of-type

是一个变形的语法糖,S:nth-of-type(An+B)是:nth-child(|An+B| of S)的另一种写法,仅使用兄弟元素中的S元素来进行排序查询。

<head>
  <style> 
  p:nth-of-type(2n){
      height:20px;
      background:#ff0000;
  }
  </style>
</head>
<body>
  <div>11111</div>
  <p>22222</p>
  <p>33333<span>44444</span></p>
  <p><!-- I will be lime --></p>
  <div>55555</div>
  <p>66666</p>
</body>

效果如下:

以此类推,还有nth-last-of-type、first-of-type、last-of-type、only-of-type。详细可以参考MDN

链接与行为伪类选择器

  • any-link 表示任意的链接,包括href属性的a、area和link标签都可能匹配到这个伪类。

这是一个实验中的功能
此功能某些浏览器尚在开发中,请参考浏览器兼容性表格以得到在不同浏览器中适合使用的前缀。

  • :link 表示未访问过的链接。

为了可以正确地渲染链接元素的样式,:link伪类选择器应当放在其他伪类选择器的前面,并且遵循LVHA的先后顺序,即::link — :visited — :hover — :active。

  • :visited 表示已经访问过的链接。

  • :hover 表示鼠标悬停在上的元素。

  • :active 表示用户正在激活这个元素,如用户按下按钮,鼠标还未抬起时,这个按钮就处于激活状态。

  • :focus 表示获得焦点的元素(如表单输入)。当用户点击或触摸元素或通过键盘的 “tab” 键选择它时会被触发。

  • :focus-within 表示一个元素获得焦点,或,该元素的后代元素获得焦点。换句话说,元素自身或者它的某个后代匹配:focus伪类。

在Selector Level 4中提出,需要注意兼容性问题。

  • :target 用于选中浏览器URL的hash部分所指示的元素,该元素的id与当前URL片段匹配。
// css
:target {
  border: 2px solid black;
}

// url
http://www.example.com/index.html#section2

在上述场景中,:target可以匹配下面的元素(id为section2):

<section id="section2">Example</section>
  • :checked 表示任何处于选中状态的radio(), checkbox() 或select元素中的option元素。

  • :disabled 和 :enabled 表示任何被禁用的元素。如果一个元素不能被激活(如选择、点击或接受文本输入)或获取焦点,则该元素处于被禁用状态。与之相对的,元素有一个启用状态,可以使用:enabled来选择。

逻辑伪类选择器

  • :not 用来匹配不符合一组选择器的元素。由于它的作用是防止特定的元素被选中,它也被称为反选伪类(negation pseudo-class)。
/* 选择所有不是段落(p)的元素 */
:not(p) {
  color: blue;
}

注意:

  • :not() 伪类不能被嵌套,这意味着 :not(:not(...)) 是无效的。
  • 由于伪元素不是简单的选择器,他们不能被当作 :not() 中的参数,形如 :not(p::before) 这样的选择器将不会工作。
  • 可以利用这个伪类提高规则的优先级。例如,#foo:not(#bar)#foo会匹配相同的元素,但是前者的优先级更高。
  • :not(.foo) 将匹配任何非 .foo 的元素,包括 和 。
  • 这个选择器只会应用在一个元素上,无法用它来排除所有父元素。比如,body :not(table) a依旧会应用到表格元素<table>内部的<a>上, 因为<tr>将会被:not(table)这部分选择器匹配。

在选择器4级标准,则允许:not接受一个选择器列表,这意味着选择器支持嵌套,仅靠:not即可完成选择器的一阶真值逻辑完备,但目前还没有看到浏览器实现它。

  • 其他 在Selector Level 4草案中,还引入了:is:where:has等逻辑伪类,都是实验性质的,需要慎重。

其它伪类选择器

还有一些草案中或者不常用的选择器,需要大概了解即可。

  • 国际化:用于处理国际化和多语言问题

    • :dir() 实验性质,匹配特定文字书写方向的元素
    • :lang 基于元素语言来匹配页面元素
  • 音频/视频:用于区分音视频播放状态

    • play
    • pause
  • 时序:用于配合读屏软件等时序性客户端的伪类

    • current
    • past
    • future
  • 表格:用于处理table的列的伪类

    • nth-col
    • nth-last-col

复杂选择器

在CSS规则中,选择器部分是一个选择器列表。

根据选择器列表的语法,我们可以将选择器的连接方式可以理解为像四则运算一样有优先级:

  • 第一优先级(复合选择器)
    • 无连接符号
  • 第二优先级(复杂选择器)
    • “空格”
    • “~”
    • “+”
    • “>”
    • “||”
  • 第三优先级(选择器列表)
    • “,”

复杂选择器是针对节点关系的选择,下面简单描述下使用的连接符号:

  • 空格:查找后代,表示选中所有符合条件的后代节点
  • >:子代,表示选中符合条件的子节点
  • ~: 后继,表示选中所有符合条件的后继节点,后继节点即跟当前节点具有同一个父元素,并出现在它之后的节点
  • +: 直接后继,表示选中符合条件的直接后继节点,直接后继节点即nextSlibling
  • ||: 列选择器,表示选中对应列中符合条件的单元格
<head>
  <style> 
  	.p1 span{
		color:#ff0000;
	}
	.p2>span {
		color:#ff0000;
	}
	.p3~p {
		color:#ff0000;
	}
	.p3+p {
		color: blue;
	}
  </style>
</head>
<body>
  <p class="p1">p1:<span>这是子节点信息<span>这是后代信息</span></span></p>
  <p class="p2">p2:<span>这是子节点信息<p>这是后代信息</p></span></p>
  <p class="p3">p3:</p>
  <p class="p4">p4</p>
  <p class="p5">p5</p>
</body>

效果如下:

针对||选择器的示例可以参考这里

选择器的优先级

CSS标准用一个三元组 (a, b, c) 来构成一个复杂选择器的优先级。

  • id选择器的数目记为a;
  • 伪类选择器和class选择器、属性选择器的数目记为b;
  • 伪元素选择器和标签选择器数目记为c(伪元素见下文);
  • “*” 不影响优先级。

CSS标准建议用一个足够大的进制,获取“ a-b-c ”来表示选择器优先级。

specificity = base * base * a + base * b + c

这里有几个点需求注意:

  • 同一优先级的选择器遵循“后面的覆盖前面的”原则
  • 选择器的优先级是针对复杂选择器的优先级,选择器列表不会合并计算优先级
  • 行内属性的优先级永远高于CSS规则
  • 在选择器前加上“!import”,会高于行内属性的优先级

伪元素

伪元素是一种重要的简单选择器,也是一种机制。伪元素的语法跟伪类相似,但是实际产生的效果却是把不存在的元素硬选出来。

下面是一些兼容性可用的伪元素,所有的伪元素列表可以参考MDN

  • ::first-line

选中块级元素的第一行元素。这里的第一行,指的是排版后显示的第一行,取决于很多因素,包括元素宽度,文档宽度和文本的文字大小,跟HTML代码中的换行无关

::first-line伪元素只能在块容器中,所以,::first-line伪元素只能在一个display值为blockinline-blocktable-cell或者table-caption中有用。在其他的类型中,::first-line是不起作用的。

CSS标准规定了::first-line必须出现在最内层的块级元素之内。在下面的情况中,由于第一行是span元素,::first-line是不生效的。

<div>
  <span id=a>First paragraph</span><br/>
  <span>Second paragraph</span>
</div>

// css
div::first-line { 
    color:blue; 
}

此外,CSS标准只要求::first-line实现有限的几个CSS属性,都是文本相关(详细信息可以参考这里)。

  • ::first-letter

选中某块级元素第一行的第一个字母,并且文字所处的行之前没有其他内容(如图片和内联的表格)。

首行只在block-container box内部才有意义, 因此::first-letter只在display属性值为block, inline-block, table-cell, list-item或者table-caption的元素上才起作用。其他情况下, ::first-letter毫无意义。

::first-line不同,::first-letter能出现在span内。

<div>
  <span id=a>First paragraph</span><br/>
  <span>Second paragraph</span>
</div>

// css
div::first-letter { 
    color:blue; 
}

这种情况下,首行的第一个字母能正常显示蓝色。

此外,CSS标准只要求::first-letter实现有限的几个CSS属性,都是文本相关(详细信息可以参考这里)。

  • ::before 和 ::after 和上面两种伪元素不同,::before表示在元素内容之前插入一个虚拟的元素,::after则表示在元素内容之后插入,都是创造出一个新的元素。

这两种伪元素,通常会配合content属性来为该元素添加装饰内容。

<p class="special">I'm real element</p>
.special::before {
    display: block;
    content: "pseudo! ";
}

::before::after还支持contentcounter

<p class="special">I'm real element</p>
.special::before {
    display: block;
    content: counter(chapno, upper-roman) ". ";
}

配合clear属性,也可以用于清除浮动。

.clear:after{
	content:"";
    display: block;
    clear: both;
}

总结

选择器可以大致分为四类:简单选择器、复合选择器、复杂选择器、选择器列表。

使用复合选择器、复杂选择器、选择器列表时,选择器的连接方式像四则运算一样有优先级。

伪元素也作为一种简单选择器,有其独有的机制,实际产生的效果是把不存在的元素硬选出来。