引言
许多小伙伴即将面临找工作,正在为面试忙碌准备,但在日常开发中,常因依赖框架而逐渐忽略了一些基础知识,比如——CSS 选择器以及其优先级权重,你还记得吗? 如果你也遇到过样式冲突而找不到原因,或者面试时被问到选择器优先级的计算而手忙脚乱,那么这篇文章正是为你而写!
接下来,我们将从基础规则讲起,带你逐步了解CSS选择器的概念、掌握 CSS 选择器的优先级权重计算,轻松应对日常开发和技术面试!
本篇文章将主要讲述CSS选择器。由于篇幅限制,如果是想看关于CSS选择器的优先级权重计算内容的小伙伴,请移步我的另外一篇文章:
CSS 选择器和优先级权重计算这么简单,你还没掌握?一篇文章让你轻松通关面试!(下)
正文
CSS选择器
什么是CSS选择器?
CSS 选择器规定了 CSS 规则会被应用到哪些元素上。如果你想要将一些特定的样式添加给html页面中的某些元素,那就需要CSS选择器来帮忙指向要作用的标签。
以下是CSS选择器的分类:
- 基本选择器
- 标签(元素)选择器
- 类选择器
- id选择器
- 通配符选择器
- 复合选择器
- 交集(并列)选择器
- 并集选择器
- 兄弟选择器
- 属性选择器
- 后代选择器
- 伪类选择器
- 伪元素选择器
基本选择器已经能够帮助我们选中所有的选择器,但复合选择器在基本选择器的基础上做了很多优化。
基本选择器
1. 标签(元素)选择器
也许你会注意到,这个选择器有两个名字,这是因为通常我们习惯于称它为标签选择器,但MDN官方文档中把它叫做元素(element)选择器。
<style>
p{
color: red;
}
</style>
<body>
<h1>标题元素</h1>
<p>段落元素1</p>
<p>段落元素2</p>
<span>span元素</span>
<div>div元素</div>
</body>
- 语法:
tagName{} - 优点:简单易懂。一眼就能知道把
<p></p>中的内容的颜色改成了红色。 - 缺点:无法区分同样标签的样式。比如在这里有两个
<p></p>,如果仅仅使用标签选择器,就无法做到仅改变段落元素1的样式。
2. 类选择器
<style>
.xxx{
color: blue;
}
</style>
<body>
<h1>标题元素</h1>
<p>段落元素</p>
<p class="xxx">段落元素</p>
<span class="xxx">span元素</span>
<div>div元素</div>
</body>
- 语法:首先给项目中的一个标签加上属性:例如
<p class="xxx"></p>,再用.xxx(类名){}。 - 优点:可以跨标签名分类。比如这段代码中,由于添加了属性,既可以改变
<p></p>中的元素,又可以改变<span></span>中的元素。
3. id选择器
- 语法:首先给项目中的一个标签起一个独一无二的id,例如
<p id="xx"></p>,再用#xx(id名){}。
细心的小伙伴已经发现了,这个语法和类选择器非常像,是的没错,他们的主要区别就在于id选择器中为标签起的id名是唯一的,这也意味着id选择器只能指向唯一的标签。
4. 通配符选择器
- 语法:它和以上都不太一样,通配符选择器不需要标签名,也不用通过任何的属性,一个
*{}就能掌握全局。所以它一般用于页面的初始化。
复合选择器
1. 交集(并列)选择器
从名字就大概能猜到了,这个选择器一看就和我们高中就学过的交并集中的交集有关。多说无益,来看下面的代码吧:
<style>
p.person{
color: blue;
}
</style>
<body>
<p class="person">张三</p>
<div class="person">李四</div>
<p class="animal">小羊苏西</p>
<div class="animal">黑猫警长</div>
</body>
在代码中我们可以看到,如果我想选中黑猫警长,直接用标签选择器会一起选中李四,用类选择器又会选中小羊苏西,这可怎么办呢?
这个时候就需要我们的交集选择器出场啦,我们可以使用它来选中标签是p且类为animal的元素。
- 语法:
xx(标签名/类名/id名)xxx(标签名/类名/id名){},要注意,这两个属性是紧紧贴在一起的,中间没有空格。
2. 并集选择器
同样的,听名字也听得出来,肯定和并集差不多,事实也确实如此。我们拿上面的代码举例:
<style>
p,
div{
color: blue;
}
</style>
<body>
<p class="person">张三</p>
<div class="person">李四</div>
<p class="animal">小羊苏西</p>
<div class="animal">黑猫警长</div>
</body>
这个时候我如果想选择所有标签为p或者div里的元素,我就可以使用并集选择器。
- 语法:
xxx(标签名),xx(标签名){}
3. 兄弟选择器
是兄弟,但也分亲兄弟和表兄弟。兄弟选择器也分选择一个兄弟还是所有兄弟。
注意!兄弟之间的地位是平等的,只有并列关系,同等级别,才能叫做兄弟,如果有嵌套关系或其他非并列关系,那就不能算是兄弟,也就无法选中。
我们来看下面这段代码:
<body>
<h1>大标题</h1>
<p>副标题</p>
<p>段落元素</p>
<span>一个元素</span>
<div>不知道起什么名字了元素</div>
<div class="div">div元素</div>
<p>p元素</p>
</body>
(1) 通过+找到身后紧邻的一个兄弟
如果这时我想要选中副标题这个元素,如果直接用标签选择器显然是不现实的,会一起选中段落元素,所以这时我可以使用兄弟选择器,选择h1紧邻的一个p标签中的元素。
<style>
h1+p{
color: aliceblue;
}
</style>
- 语法:
xxx(标签名/类名/id名)+xx(标签名/类名/id名){}
(2) 通过~找到身后紧邻的所有兄弟
有时我们会不止想选中一个兄弟,有福得大家一起享,有活得大家一起干。这时可以用 ~ 选择符。一举选中 h1 后面所有的 p 元素兄弟。
你可能会好奇:最下面的 p 和 h1 并不紧邻,为什么也被选中了?
这是因为 ~ 会匹配 h1 后所有的兄弟标签,再从中筛选出符合条件的 p 元素。因此,h1 后的所有 p 都会被选中,无论是否紧邻。
<style>
h1~p{
color: aliceblue;
}
</style>
- 语法:
xxx(标签名/类名/id名)~xx(标签名/类名/id名**){}
4. 属性选择器
属性选择器是一种通过元素属性的值来选中元素的选择器,根据不同的需求可以分为以下几种情况:
- 选中带有某个属性的元素:
[属性名]{} - 选中属性值完全匹配的元素:
[属性名=属性值]{} - 选中属性值以某个字符串开头的元素:
[属性名^=值]{} - 选中属性值以某个字符串结尾的元素:
[属性名$=值]{} - 选中属性值包含某个字符串的元素:
[属性名*=值]{} - 选中属性值包含某个独立单词的元素:
[属性名~=值]{}
(1) 带某种属性
如果你想选中所有带有 class 属性的元素,就可以使用 [class] ,一下就能选中带有class的 <p> 和 <span>:
<style>
[class] {
color: blue;
}
</style>
<body>
<h1>标题</h1>
<p class="highlight">这是一个段落</p>
<p>这是另一个段落</p>
<span class="highlight">这是一个 span 元素</span>
<div data-type="info">这是一个 div 元素</div>
<div data-type="error">这是另一个 div 元素</div>
</body>
- 语法:
[属性名]{}
(2) 属性值完全匹配
如果需要精确匹配属性值,比如选中 class="highlight" 的元素,可以使用 [class="highlight"]:
<style>
[class="highlight"] {
font-weight: bold;
}
</style>
<body>
<p class="highlight">这是一个段落</p>
<p>这是另一个段落</p>
<span class="highlight">这是一个 span 元素</span>
<div data-type="info">这是一个 div 元素</div>
</body>
- 语法:
[属性名=属性值]{}
(3) 属性值以某个字符串开头
想选中所有 data-type 属性值以 info 开头的元素,就可以用 [data-type^="info"]:
<style>
[data-type^="info"] {
background-color: lightgreen;
}
</style>
<body>
<div data-type="info">这是一个 div 元素</div>
<div data-type="error">这是另一个 div 元素</div>
</body>
- 语法:
[属性名^=值]{}
(4) 属性值以某个字符串结尾
如果想选中 data-type 属性值以 error 结尾的元素,可以使用 [data-type$="error"]:
<style>
[data-type$="error"] {
color: red;
}
</style>
<body>
<h1>标题</h1>
<p class="highlight">这是一个段落</p>
<div data-type="info">这是一个 div 元素</div>
<div data-type="error">这是另一个 div 元素</div>
</body>
- 语法:
[属性名$=值]{}
(5) 属性值包含某个字符串
有时候我们需要判断属性值是否包含某些字符,比如选中所有 data-type 属性值中带有 type 的元素,就可以使用 [data-type*="type"]:
<style>
[data-type*="type"] {
border: 1px dashed gray;
}
</style>
<body>
<p class="highlight">这是一个段落</p>
<span class="highlight">这是一个 span 元素</span>
<div data-type="info">这是一个 div 元素</div>
<div data-type="error">这是另一个 div 元素</div>
</body>
(6) 属性值包含某个独立单词
这和“(5) 属性值包含某个字符串”有点相似,但二者存在差别:~= 会将属性值视为一组由空格分隔的单词,仅匹配独立的单词,而 *= 则会匹配属性值中任何位置出现的指定的字符串,无论是否是独立单词。
比如需要选中 class 属性值中包含 highlight 这个独立单词的元素,就可以使用 [class~="highlight"],如此就只会选中第一项,而不会选中第二项:
<style>
[class~="highlight"] {
background-color: yellow;
}
</style>
<body>
<ul>
<li class="item highlight">第一项</li>
<li class="item">第二项</li>
<li class="itemhighlight">第三项</li>
<li class="high">第四项</li>
</ul>
</body>
- 语法:
[属性名~=值]{}
5. 后代选择器
后代选择器可以选择一个元素的儿子、孙子、重孙子... ...子子孙孙世世代代无穷尽也!分为选直接后代和所有后代。
(1) 直接后代
可以选中某元素的直接子元素,也就是儿子。只匹配紧邻一级的后代,而不会匹配更深层的嵌套元素。
比如在这里就只会选中.div 的直接子元素中 class="p" 的 div子代p1。
<style>
.div > .p {
color: aqua;
}
</style>
<body>
<div class="div">
<h1>div子代h1</h1>
<p class="p">div子代p1</p>
<p>div子代p2</p>
<div>
<span>div中的div中的span</span>
<p>div中的div中的p</p>
</div>
<div>div中的div</div>
</div>
</body>
- 语法:
xxx(标签名/类名/id名)>xxx(标签名/类名/id名)
(2) 所有后代
可以选中某元素的所有后代,包括儿子、孙子、重孙... ...
比如这里,就可以选中 .div 的所有后代中 class="p" 的 <p>,也就是 div子代p1 和 div中的div中的p。
<style>
.div .p {
color: aqua;
}
</style>
<body>
<div class="div">
<h1>div子代h1</h1>
<p class="p">div子代p1</p>
<p>div子代p2</p>
<div>
<span>div中的div中的span</span>
<p>div中的div中的p</p>
</div>
<div>div中的div</div>
</div>
</body>
- 语法:
xxx(标签名/类名/id名) xxx(标签名/类名/id名)
这里中间有空格的噢!不要忽略了!
6. 伪类选择器(不是类,很像类)
伪类选择器选中的不是元素,而是元素的某种状态或描述,这正好对应了伪类中两个细致的小分支:动态伪类和结构伪类。
- 语法:通常在用基本选择器选中元素后,在后通过
:xxx(伪类名)的形式添加伪类。
动态伪类中一共有四种不同的状态:
1. link:没有被访问过。
2. visited:被访问过的。
3. hover:鼠标悬停的状态。
4. active:被激活的状态,也就是用鼠标按住不放的状态。
link和visited只有a链接才有。
<style>
a:link {
color: blue;
}
a:visited {
color: purple;
}
a:hover {
text-decoration: underline;
}
a:active {
color: red;
}
</style>
<body>
<a href="2.html" target="_blank">我是一个a链接</a>
</body>
- 关于顺序的注意事项:动态伪类之间的顺序很重要,如果你要同时写这几个伪元素的话,建议按照
:link→:visited→:hover→:active的顺序。这样可以确保伪类效果不被覆盖。例如,如果将:hover写在:link或:visited前面,鼠标悬停时颜色可能会失效,因为后面的规则会覆盖之前的样式。
结构伪类种类就很多了,由于在日常开发中并不常见,所以不赘述,在这里给大家大致列出:
first-child:选中父元素的第一个子元素。
last-child:选中父元素的最后一个子元素。
nth-child(n):选中父元素的第n个子元素,n可以是以下形式:
- 从 0 开始,理论上可到正无穷。
- 超出范围(如 0 或大于元素数量)则无法匹配。
- 如果直接写
n,则会匹配所有子元素。- 支持公式表示,如:
2n或even:匹配偶数子元素。2n+1或odd:匹配奇数子元素。-n+5:匹配前 5 个子元素。- 倒序通过计算公式反推出。
first-of-type:选中父元素中特定类型的第一个子元素。
last-of-type: 选中父元素中特定类型的最后一个子元素。
nth-of-type(n): 选中父元素中特定类型的第n个子元素。
nth-last-child(n): 选中父元素从后往前数的第n个子元素。
nth-last-of-type(n): 选中父元素中特定类型从后往前数的第n个子元素。
only-child: 选中父元素中唯一的子元素。
only-of-type: 选中父元素中特定类型的唯一子元素。
root: 选中文档的根元素,通常为<html>。
empty: 选中没有任何子节点(包括文本节点)的元素。
UI伪类
checked: 选中被点击的单选按钮或复选框。disabled: 选中被禁用的元素(如禁用的输入框)。enabled: 选中可用的元素(如未禁用的输入框)。
7. 伪元素选择器(不是元素,很像元素)
- 语法:和伪类选择器一样,伪元素选择器选中的当然也不是元素,你可以把它理解为是某些元素的某些位置。通过
::(伪类名)实现。常用的伪元素有::before、::after、::first-letter、::first-line。
(1) ::before、::after:在元素之前或之后插入内容
假设我们需要在商品价格前加上美元符号 $,或者在书籍名称前后加上书名号 《》,就可以使用伪元素 ::before 和 ::after 实现,这个时候可以发现当我们鼠标去选中它们时,无法选中美元符号和书名号,只能选中里面的文字:
<style>
span::before {
content: "$";
}
span.book::before {
content: "《";
}
span.book::after {
content: "》";
}
</style>
<body>
<span>999.9</span>
<span class="book">论语</span>
</body>
- 语法:
::before在元素内容前插入内容;::after:在元素内容后插入内容。
(2) ::first-letter:选择元素的首字母
有时候为了美观,段落的首字母需要设置为大号字体,可以使用 ::first-letter:
<style>
p::first-letter {
font-size: 20px;
color: red;
}
</style>
<body>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Perspiciatis, atque, labore inventore rerum quae ullam asperiores obcaecati placeat nisi quisquam tempora eveniet, error nihil doloremque ea architecto aperiam id ipsum.</p>
</body>
(3) ::first-line:选择元素的第一行文本
如果需要给段落的第一行文本设置什么样式,可以使用 ::first-line。它会选中段落的第一行文字,无论内容如何折行。这里就不再举例了,和上面 ::first-letter是一样的。
结语
由于篇幅限制,本篇文章暂时写到这里,如果感兴趣,可以进入我的主页,在下篇文章和我一起继续逐步深入掌握CSS选择器的优先级权重计算!
如果你在阅读过程中发现任何问题或有其他的想法,欢迎交流探讨!也祝正在准备面试的小伙伴们都能拿到自己心仪的 offer,加油!🎉