CSS 有一个简明易懂的结构:一个 CSS 文件就是一个样式表,一个样式表中有多 个样式,一个样式中有选择器和样式规则两部分,一个样式规则包含样式属性和样式值两部分……作为一名前端工程师,CSS 在个人的职业技能树上占有非常重要的地位,是核心技能之一。在过去的一年多时间中,我很多的工作都是围绕 JS 展开的,希望趁最近的时间做一些 CSS 方面的复习和总结,先从选择器入手,介绍已经被大多数浏览器所支持的 CSS 1 ~ CSS 3 选择器。如果你想知道自己的浏览器支持哪些选择器,可以点击 http://css4-selectors.com/browser-selector-test/ 进行在线测试。本文中介绍的选择器实践经验,大多来自于文末的参考资料,推荐各位学习或复习。

选择器

目前最新的浏览器全部支持 CSS 3 及之前的选择器,这些选择器的总数在四十以内,下面我们一个个介绍这些选择器的用法和常见误区。

通配符选择器得名于其使用的符号 *,它可以用于选择文档中的所有元素,但不能选择伪元素。此外,还有一种无效情况:

<div>
<p>Lorem ipsum dolor sit amet...</p>
</div>

在上述 HTML 结构中,p 是 div 的直接子级,如果开发者使用如下样式,则找不到相应的元素:

div * p { color: red; }

这是因为 CSS 中的通配符选择器 * 不能为空,而我们在正则表达式中使用的 * 则可以表示空,这两者之间的区别需要小心对待。

元素选择器、类选择器、ID 选择器几乎是必不可少的选择器,相信大家已经对它们谙熟于胸了:

p { color: red; }
.red { color: red; }
#logo { color: red; }

如果一个页面有多个 id="logo" 的元素,那么 #logo { color: red; } 会对这些元素生效吗?在 chrome canary 54 上答案是会的,但不建议这样使用 ID。

属性选择器可以根据元素属性进行选择,上述的类选择器和 ID 选择器可以使用属性选择器来模仿(模仿后功能相似,但权重不同),属性选择器包含以下几种类型:

  • [class="red"],匹配 class 属性等于 red 的元素
  • [class~="red"],匹配 class 属性中包含 red 单词的元素,class="red danger tip" 是有效的,class="redius" 则是无效的
  • [class|="red"],匹配 class 属性的值以 red 开头的元素
  • [class^="red"],匹配 class 属性的值以 red 开头的元素
  • [class*="red"],匹配 class 属性的值包含 red 字符串的元素
  • [class$="red"],匹配 class 属性的值以 red 结尾的元素

这里的 [class|="red"][class^="red"] 相似,区别在于,[class|="red"] 属性值不能包含特殊字符,在 chrome cannary 54 测试只能是数字或字母。制定 [class|="red"] 选择器的初衷是为了匹配语言子码,比如下面的样式对 lang 属性值为 en / en-US / en-GB 元素都有效:

[lang|=en] { color: red; }

上面介绍的选择器都是单一使用的选择器,更实用的方式是将多个多种选择器组合起来,对文档元素进行精确定位:

  • div p,后代选择器,浏览器解析选择器时按照从右往左的顺序进行选择,所以这里会先找出所有的 p 元素,然后找出 p 元素之上有 div 元素的 p 元素
  • div > p,直接后代选择器,在这里就是要找出所有直接子元素是 p 元素的 div 元素
  • div + p,相邻元素选择器,在这里选择的 p 元素有两个要求:与 div 元素同级且相邻,中间没有其他元素;在 HTML 文档中位于 div 元素之后,最终会选择每个 div 元素之后的一个 p 元素
  • div ~ p,同类选择器,和相邻选择器相似,不同之处在于,这里选择的 p 元素不必与 div 元素相邻,只需要在 HTML 文档中位于 div 元素之后即可,最终会选择每个 div 元素之后的多个 p 元素

伪类元素的特殊性在于它们是动态存在的,只有用户触发了某些事件(鼠标悬停、移入移出等)才会生效,常见的伪类选择器包括::link:visited:focus:hover:avtive,需要注意的是在使用的时候,它们的声明顺序会影响页面效果,这是因为它们具有相同的权重,有关权重的问题我们会在下一节介绍。

下面是一些和元素位置相关的伪类选择器:

  • li:first-child,这里选中的 li 元素必须是其父级的第一个子元素
  • li:last-child,这里选中的 li 元素必须是其父级的第一个子元素
  • li:only-child,这里选中的 li 元素必须是其父级的唯一子元素
  • li:nth-child(N),这里的 N 可以是表达式(2n+1 / -n+1 …)、odd、even,选中的 li 元素必须是其父级的第 N 个子元素
  • li:nth-last-child(N):这里选中的 li 元素必须是其父级的倒数第 N 个子元素
  • li:first-of-type,这里选中的 li 不一定是父级的第一个子元素,但一定是父级的第一个 li 元素
  • li:last-of-type,这里选中的 li 不一定是父级的最后一个子元素,但一定是父级的最后一个 li 元素
  • li:only-of-type,这里选中的 li 不一定是父级唯一的子元素,但一定是父级唯一的 li 元素
  • li:nth-of-type(N),这里选中的 li 不一定是父级的第 N 个子元素,但一定是父级的第 N 个 li 元素
  • li:nth-last-of-type(N),这里选中的 li 不一定是父级的倒数第 N 个子元素,但一定是父级的倒数第 N 个 li 元素

上述以 -child 结尾的选择器,往往对元素在 DOM 结构中的位置和数量有严格要求,以 -of-type 结尾的选择器则要宽松很多。

其他伪类选择器:

  • :root,在 HTML 文档中,匹配 html 元素
  • :empty,匹配那些没有子元素的元素,比如 <p></p> 就没有子元素,但是 <p> </p> 是有子元素的
  • :target,该选择器和 URI 有关,如果 URI 是 http://a.com/index.html#abc,那么匹配的就是页面上 ID 属性值为 abc 的元素
  • :enabled,大多用于表单,选择所有未被禁用的元素,未被禁用的元素可以接受焦点,可以被激活,可以输入文本
  • :disabled,大多用于表单,选择所有禁用的元素,禁用的元素通常不能接受焦点,不能被激活,不能被单击或输入文本
  • :checked,大多用于表单,选择所有 selected 或 checked 元素
  • :not(S),否定伪类选择器,这里的 S 可以是其他选择器,比如 :not(p:empty) 选中了非空的 p 元素
  • :lang,语言规范选择器,使用该类的前提是 HTML 元素上设置了 lang 属性,该选择器会根据该属性的值进行匹配,匹配成功则选中

最后是伪元素选择器,它们所创建的元素也是动态和虚拟的,其内容可以在触发某些事件时动态生成,目前(CSS 3 以之前)一共有五种伪元素选择器:

  • ::first-letter,通俗来说,该选择器用于选择块级元素的第一行的第一个字符。严格来说,选择块级元素、内联块元素、表格标题、表格单元格或列表项中的第一个已格式化的文本行
  • ::first-line,通俗来说,该选择器用于选择块级元素的第一行
  • li::before,在另一个元素之前生成一个伪元素,值得注意的是,它只会渲染某些内容,但不会称为 DOM 树上的真实节点
  • li::after,在另一个元素之后生成一个伪元素,值得注意的是,它只会渲染某些内容,但不会称为 DOM 树上的真实节点
  • ::selection,选择用户选中的文档元素,常用于自定义用户选择部分内容的样式,该选择可用的样式并不多,最新浏览器都支持 color 和 background 属性,其他的属性具有兼容性问题

权重

当有多个选择器指向同一个 HTML 元素时,它们之间就会发生竞争,争取成为最后生效的样式。既然有竞争,就会有相应的判定规则,这个规则的核心就是不同类型的选择器具有不同的权重,下面的选择器权重从上到下依次减弱:

  • !important 拥有最高优先级
  • <style></style> 内置样式
  • ID 选择器
  • 类、属性、伪类、伪元素选择器
  • 元素选择器
  • 通配符选择器

当根据以上顺序比较权重,结果相同时,会继续比较选择器出现的前后顺序,晚出现的选择器会覆盖早出现的选择器,即使它们的权重相同。为了简化对权重的计算,我们可以按照以下顺序编写 CSS 演示:

/* 通配符选择器 */
/* 元素选择器 */
/* 类、属性、伪选择器 */
/* ID 选择器 */

实际开发中使用场景多变,还需要根据实际情况适当调整。Chris Coyier 在 《Specifics on CSS Specificity》 中使用了可量化的方式衡量样式的权重,有兴趣地可以前往学习。

参考资料