一、选择器的种类
1、基本选择器
基本选择器包含:通配选择器、元素选择器、ID选择器、类选择器和群组选择器。
对于类选择器的命名,'-'和'_'两者多有,具体可以看这里
通配选择器(*),它用来选择所有元素,不过这个选择器的效率比较低,所以慎重选择,但是瞅了一眼Boostrap4.0是这样设置box-sizing:
html { box-sizing: border-box; } *, *::before, *::after { box-sizing: inherit; }
所以吧,也不是不能用,不过也就这一处,还是得少用。
ID选择器,它的唯一性是规范,但是应用于多个元素,样式同样生效,不推荐这样做。
群组选择器,通过逗号隔开,可以有效的减少一些冗余代码,例如上面的例子中:
*, *::before, *::after { box-sizing: inherit; }
2、属性选择器
属性选择器主要针对于元素的属性值进行筛选,包含这几种操作符:=、|=、~=、*=、^=和$=:
[attr=val]:属性值为val;
[attr|=val]:属性值为val或者以val-开头;
[attr~=val]:属性值为val或者以多个空格分开含有val;
[attr*=val]:属性值包含val;
[attr^=val]:属性值以val开头;
[attr$=val]:属性值以val结尾。
属性选择器的应用很多,例如Vue中的scoped css也正是利用属性选择器实现的。
3、层次选择器
层次选择器包含后代选择器(E F)、子选择器(E > F)、相邻兄弟选择器(E + F)和通用选择器(E ~ F)。
后代选择器与子选择器的区别:相比较前者,子选择器的搜索范围局限于儿子节点。
相邻兄弟选择器,选择E后面相邻的兄弟节点F。很遗憾不能选择前面的。这个选择器的应用也很多,比如我们设置列表项之间的距离为20px:
li:not(:last-child) + li { margin-top: 20px; }
通用选择器,选择E后面所有的兄弟节点F。同样上面的例子:
li:first-child ~ li { margin-top: 20px; }
如果你能理解上面这两种写法,基本上这两个选择器你就理解了。
4、伪元素选择器
首先对于伪元素的写法,标准是推荐双冒号,感觉这样写挺好的,可以和伪类区别开来。
::first-letter作用于整块文本的第一段的第一个文字,元素必须为块级元素。例如修饰段落第一个单词的样式:
p::first-letter { color: red; font-size: 20px; }
::first-line则是作用于整块文本的第一段,同样必须为块级元素。
::before和::after分别向元素的前后添加内容,用处很大,比如我们实现这样的购物车:
购物车
<span role="btn" aria-label="购物车" attr-quantity="2" class="caricon"></span>
/* 这里就不展现无关紧要的样式了 */ .caricon { /* 这里设置容器的样式 */ } .caricon::before { /* 通过伪元素设置购物车图标 */ } .caricon::after { /* 这两个伪元素多有一个重要的content属性,用来设置伪元素的内容 这里通过attr获取元素的属性值,实现购物车数量 */ content: attr(attr-quantity); }
在一些网站中,当你鼠标选择文字会发生样式变化,这是因为用了::selection,不过它能改变的CSS属性比较有限,具体可以查询MDN。
还有我们讨厌input的placeholder样式太丑时,你可以使用::placeholder来修改,但是这是一个试验性的伪元素,还有很多试验性的伪元素,有兴趣可以搜索一下。
5、伪类
伪类的组成就更丰富了,这里选择两个比较重要可能还有点容易混淆的说说。
首先看看否定伪类选择器:not,在前面的例子中你已经看到它的身影了,它匹配不符合描述的元素,用处可以说很大。
然后就是结构伪类选择器,这里的内容也多,而且很容易混淆。
例如:first-child和:first-of-type:
p:first-child { /* 计算范围:所有的兄弟节点 */ } p:first-of-type { /* 计算范围:兄弟节点中所有p元素 */ }
同样还有:last-child和:last-of-type,就很容易懂了。
:nth-child就比较强大了,不仅可以使用odd和even,还可以使用表达式。例如我们列表的斑马线:
li:nth-child(even) { background: red; }
关于:nth-child重要的几点可以总结为:
值的下标是从1开始的;
但是n的下标是从0开始;
当表达式的值为0或者负数是无效的;
采用表达式可以实现很多需求,例如我只想让列表的前三项设置样式:nth-child(-n + 3);
像什么:nth-of-type,:nth-last-child和:nth-last-of-type是不是小case了。
:only-child表示父节点只有一个子节点,其实学习了上面的一些选择器,我们完全可以找到等价的表达方式:
li:first-child:last-child { background: yellow; }
当然还有:only-of-type是必不可少的。
二、选择器的优先级
上面这么多的选择器,是不是很头大,这也是为什么这里要特地说一下选择器优先级。当多条样式应用在一个元素上,系统会将样式合并,当一个属性被赋了多次值,到底取哪个值呢?这时候就需要优先级了,优先级高的覆盖优先级低的。
选择器优先级规则:
首先通配符是没有权重的(所以我感觉这点也是不推荐使用它的原因);
!important的权重bug级的高;
权重第二高的就是元素的行内样式(style属性中的值);
接下来的情况我们可以数值化:
id选择器的权重为100;
类选择器、属性选择器和伪类选择器的权重为10;
元素和伪元素的权重为1。
如果优先级一样,则后声明的优先级高。
看个例子:
li { width: 300px; height: 90px; background: red; } ul li { background: yellow; }
首先对于没有重复赋值的width和height,并不存在什么优先级的问题,而对于background,第一个样式的优先级为1,而第二个样式的优先级为2,所以将yellow赋值给background。
三、总结
前面写了很多废话,也没有将选择器罗列完整,但是基本的构成,大家应该很清晰了。
对于后面说的选择器优先级的计算,并不是说你每次写完样式多要求算一下,最重要的目的是:你理解计算规则后,让你对每种选择器的应用场景更加清晰,而不是说随便用,从而造成意想不到的样式覆盖问题。