首页 > 编程笔记

CSS :root伪类的用法

:root 伪类用来匹配文档的根元素,本节进行详细地分析。

:root伪类匹配的究竟是什么

在 99.99% 的 Web 开发场景中,:root 伪类表示的就是 <html> 元素,二者完全等同。

例如,给 <html> 元素加一个类名,如下:
<html class="html"></html>
此时,设置背景色就可以看到整个页面的背景色变成天蓝色了:
:root.html { background: skyblue; }
或者直接使用 html 标签也可以证明:
html:root { background: skyblue; }
所以,问题来了,如果只是为了匹配 <html> 元素,那么直接使用 html 标签选择器不就可以了吗?为什么还需要额外设计 :root 伪类呢?因为在有些场景下,文档的根元素并不是 <html> 元素。

有人可能会恍然大悟:如果页面没有设置 <html> 根元素,就不会匹配!

其实,对于 HTML 页面,就算不设置 html 标签,浏览器也会自动补上 html 标签的,依然会匹配。这里描述的场景其实是浏览器访问 XML 文件或者 SVG 文件。例如,在 SVG 文件中 :root 就不等同于 html 标签了,而是 svg 标签。这个很好测试,我们随便找一个 SVG 图形元素,如果内联在 HTML 页面中,在 svg 标签匹配的是 svg:not(:root),相关代码如下:
svg:not(:root){
     overflow:hidder;
}
但是如果这个 SVG 图形作为 SVG 文件在浏览器中打开,则打开控制台就会看到 svg 标签匹配的是 svg:root,也就是此时 :root 伪类匹配的是 svg 标签,相关代码如下:
svg:root{
     width:100%;
     heigth:100%;
}
因此,:root 伪类匹配的是根元素,而 Web 中的根元素有很多种,<html> 只是其中之一。

不过,从实际 Web 开发的角度看,XHTML 语言的文档处于绝对垄断地位,因此,:root 伪类直接等同于 html 标签也是没有问题的。

另外,在 Shadow DOM 中虽然也有根的概念(称为 shadowRoot),但并不能匹配 :root 伪类,也就是在 Shadow DOM 中,:root 伪类是无效的,应该使用专门为此场景设计的 :host 伪类。

:root伪类的应用场景

由于没有特别需要使用 :root 伪类的理由,反正匹配的是 <html> 元素,因此为何不直接使用 html 标签选择器呢?这样兼容性更好,优先级更低,是这样吗?

实际上,下面两个开发场景中更推荐使用 :root 伪类。

1) 利用:root伪类的高优先级

假设引入了某些 UI 组件库,如果这些组件对 html 标签进行了一些设置,而这些设置是开发人员不需要的,我们就可以使用 :root 伪类进行重置,因为 :root 伪类的优先级更高,不用担心不能重置。

例如设置了如下的 CSS:
html{
    overflow-y: scroll;
}
我们就可以用如下代码重置,以确保页面内容在加载过程中不会出现晃动。
:root{
    overflow-y: auto;
    scrollbar-gutter: stable;
}
另外,借助 :root 伪类提高任意选择器的优先级也是一种常见的技巧,例如类名 .recover 不能重置某些样式,可以在其前面加上 :root,变成 :root .recover,说不定就可以重置了,毕竟任何页面都一定有根元素,这种写法要比 .recover.recover 的性能提高不少。

2) CSS变量

现代浏览器都已经支持 CSS 自定义属性(也就是 CSS 变量),其中有一些变量是全局的,如整站的颜色、主体布局的尺寸等。对于这些变量,业界约定俗成,都将它们写在 :root 伪类中。

大家千万不要以为将 CSS 变量写在 :root 伪类中有什么特别的作用,这只是一种写法而已,其效果和写在 html 标签选择器中是一样的,因为全局 CSS 变量一定都是用以继承的,只要是级别足够高的祖先选择器都可以。

之所以 CSS 变量都写在 :root 伪类中,可能是因为这样可以使代码的可读性更好。同样是根元素,html 标签选择器负责样式,:root 伪类负责变量,互相分离,各司其职。

例如:
:root {
   /* 颜色变量 */
   --blue: #2486ff;
   --red: #f4615c;
   /* 尺寸变量 */
   --layerWidth: 1190px;
}
html {
   overflow: auto;
}

推荐阅读