梳理选择器的种类,理解选择器的优先级,从而避免莫名其妙的样式重叠问题。
一、选择器的种类
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。
三、总结
前面写了很多废话,也没有将选择器罗列完整,但是基本的构成,大家应该很清晰了。
对于后面说的选择器优先级的计算,并不是说你每次写完样式多要求算一下,最重要的目的是:你理解计算规则后,让你对每种选择器的应用场景更加清晰,而不是说随便用,从而造成意想不到的样式覆盖问题。
最后,谢谢大家看完这篇文章。
参考资料: 图解CSS3 CSS Specificity: Things You Should Know
喜欢本文的小伙伴们,欢迎关注我的订阅号超爱敲代码,查看更多内容.