Skip to content

在 CSS 3 中新增了很多选择器,但其并没有改变选择器的原有类型,也就是说原来有哪些类型的选择器现在还是有哪些类型,只不过是在部分类型中新增了一些选择器。所以权重计算其实并没有改变。

relation selectors ​

在 CSS 中,我们学习了很多关系选择器,比如后代选择器,直接子元素选择器

css
div p{/*后代选择器*/

}

div>p{/*直接子元素选择器*/

}

其实这个直接子元素选择器就是一个 CSS 3 的选择器,这个选择器兼容性非常好,所以不太提它和 CSS 2 的区别。

除此之外,还有两个关系选择器,其实在我的 3.选择器中都介绍过,但这两个选择器用途不是很广泛,可能并不是所有读者都了解,这里再介绍一次。

css
div+p{

}

这个加号选择器可以筛选出紧接着 div 的 p 元素,也就是说要被选中的元素必须满足两个条件,必须是 p 元素,并且要紧接着 div 。

css
.test + .demo{

}

这个选择器就是在选择所有类名为 demo 的元素中紧接着类名为 test 的哪些元素,注意必须是紧挨着。

css
div ~ p{

}

这个选择器和加号那个类似,加号选择器选中的是紧挨着某个元素,这个选择器则只要出现在某个元素之后就会被选中。比如上面的例子,就会选择所有出现在 div 后面的 p 元素。

html
<div>
        <div>
            <p></p>
            <div></div>
            <p></p>
        </div>
        <p></p>
        <a href="">
            <p></p>
        </a>
        <p></p>
</div>
<p></p>

也就是说只要 p 元素前面有 div 元素作为兄弟元素,那就会被选中。

css
.test ~ .demo{

}

这个也一样,在所有类名为 demo 的元素中,只要前面的兄弟元素有类名为 test 的元素,就会被选中。反过来说也一样,就是选中类名为 test 的元素后面所有类名为 demo 的元素。

加号那个其实是 CSS 2 的选择器

attribute selectors ​

首先,在 CSS 2 中一定也学习过属性选择器。

css
div[data]{/*选择出div元素中所有包含data属性的元素*/

}
div[data="test"]{/*选择出div元素中所有data属性为test的元素*/

}

以上两个是常见的属性选择器,以下进行扩充。

css
div[data~="test"]{

}

我们知道,CSS 属性是可以按照空格进行分割的,相当于就是多个属性。

html
<div class="test demo a"></div>

在这个元素中,div 元素有一个 class 属性,class 属性的值为 test demo a,这里的属性值就被分割为了三个,分别是 test, demo, a,这就好像函数传递参数一样,属性也可以用空格分割为多个,相当于这个属性由这三部分共同组成,我可以把这三个属性称为其子属性,也就是说这个属性由三个子属性共同组成(子属性这个名是我瞎取的)。

html
<div data="test demo mode">

</div>

这个元素中的 data 属性的属性值就可以看成三个属性值共同组成,分别是 test, demo, mode,此时我们就可以使用使用这个选择器来选择。

css
div[data~="test"]{}
div[data~="demo"]{}
div[data~="mode"]{}

以上三个选择器均可以选中这个元素。也就是说这个选择器就是选中某个属性中包含某个单独子属性的元素,大概可以这么理解。

css
div[class|="a"]{}

这个选择什么呢,其实就是选中某个属性以 a 或者 a- 开头的元素。

css
div[data|="test"]{}

这个选择器选中的就是所有 div 元素中,data 属性的值以 test 或者 test- 开头的属性

html
<div data="test-a"></div>
<div data="test-b"></div>
<div data="test"></div>

以上三个元素都会被选中

以上两个属性选择器均为 CSS 2 提供的

css
div[attr^="val"]{}
div[attr$="val"]{}

这两个选择器其实我相信都不需要介绍,就是正则,第一个选择器用于选择所有 div 元素中 attr 属性以 val 开头的属性,第二个选择器用于选择所有 div 元素中 attr 属性以 val 结尾的元素,这里的符号都和正则一样,我相信都不需要再二次说明了,如果读者对这里有疑问,可以参考正则的教程。

最后一个属性选择器是这样一个东西

css
div[attr*="t"]{}

这个选择器选择的是所有 div 元素中,attr 属性存在 t 的元素,注意,只要存在 t 这个字母就行,例如属性值为 abgtgg,或者 tsdt,只要属性中有这个字母就可以。

注意和上面的 ~= 区分,~= 选择的是包含某个子属性,而 *= 选择的是存在某个字母就行。

其实后面三个 CSS 3 提供的选择器都和正则类似,用途其实也会比 CSS 2 那俩选择器广泛一点,甚至可以替代 CSS 2 那俩选择器。但实际上属性选择器本身用得都不多,更别说这几个了,只有在一些特殊场景下才会用到。

pseudo-element selectors ​

CSS 3 提供了两个新的伪元素选择器,placeholder 和 selection

先看一下 placeholder 是啥

html
<input type="text" placeholder="请输入你的名字">

055760869b6e9b85a7e84cbf42b05a6e_MD5

显而易见吧,一般来说我们写一个文本框的时候,当文本框为空的时候我们都需要给出一个浅色的提示信息,最早我们是需要用 JS 自己写的,但这个功能实在是太频繁了,几乎所有输入框都需要这个功能,所以 HTML 5 提供了 placeholder 属性帮我们自动完成了这个功能。

这里就会有一个问题,我怎么修改这个提示信息的默认样式,比如我希望它的颜色更深一点,或者让他字体加粗。

我们要给这个 placeholder 添加的提示信息修改样式,由于 placeholder 本身不是我们写的 HTML 元素,而是 input 元素里面的一个伪元素,所以肯定得是用伪元素选择器,CSS 3 提供了这样一个伪元素选择器用于帮助我们选择这个提示文本。

css
 input::placeholder {
    color: red;
 }

这样就可以将 input 的默认提示文本设置为红色,可见 ::placeholder 这个伪元素本身就是 input 里面的提示文本,我们通过这个伪元素选择器可以将提示文本选择出来,然后修改其样式。

这个 placeholder 其实很扯淡,以为这个提示文本的文字大小、内边距这些其实是和文本一起变化的,例如我将 input 元素的字体设置为 20px,那么 placeholder 里面的提示文本字体也就跟着变了,这当然也符合直觉,提示文本本身就应该和文本的尺寸保持一致,所以设置字体大小,内边距这些根本用不着 placeholder 这个选择器,直接设置 input 的样式就可以。这导致了 placeholder 选择器好像只能在设置文本颜色这里有一点用。

学完了 placeholder,剩下这个 selection 还真有用,先来看一个效果

html
<p>
     这是一个p元素
</p>

这个元素,我们看它的显示效果

f7175aefc8cbb55f221a275b96771cf3_MD5

你看,我将它选中的时候,它的底色会变蓝,文字颜色会变成白色,这个东西我们称为叫高亮,这个高亮的样式其实就是 CSS 样式设置的,我们就可以用 selection 这个伪元素选择器,这个伪元素就可以选中文本中高亮的部分。

注意,这个并不是伪类选择器,伪类选择器可以认为是处于某种状态的元素,或者说是满足某些条件的元素。伪元素则是一个元素中的一些部分,比如 ::before 就是元素的开头部分,比如 ::after 就是元素的结束部分,比如 ::selection 就是元素中高亮的部分。

我们可以使用 selection 选择器给高亮部分设置一些样式,只有一少部分 CSS 属性可以用于 selection 选择器内

  • color
  • background-color
  • cursor
  • caret-color
  • outline
  • text-decoration
  • text-emphasis-color
  • text-shadow

由于兼容性问题,一些浏览器目前可能并不支持 text-decoration 属性

css
p::selection {
   background-color: skyblue;
}

20d5ae901f41bb50f4611815b0ba89a1_MD5

这里有一个很明显的问题,原来我们没有设置样式之前,字体颜色是白色,但我们设置了背景颜色之后,字体颜色变成了黑色,同样,如果读者感兴趣,可以只设置字体颜色试一试,读者会发现如果只设置字体颜色那么背景颜色会直接消失。

css
p::selection {
	color: white;
	background-color: skyblue;
}

93f09b0cff6bf09cfe22e1055bf75d4d_MD5

这就是一个相对好看的样式

pseudo-classes selectors ​

CSS 3 提供了非常多的伪类选择器,这里逐一介绍

css
div:not(.test){

}

这个选择器用于排除,也就是从已选中的元素中,排除掉某些特定元素。以上选择器可以选择所有 div 中,类名不是 test 的元素,not 里面的参数可以填任何一个选择器。

css
:root{

}

这个伪类选择器用于选择根标签,在 HTML 中,:root 等价于 html,因为 HTML 的根元素一定是 html。但在其他的,比如 xml 中,:root 就用于选择根元素。

css
:target{
	
}

这个选择器用于选择当前锚点所标记的元素。

什么意思呢,我们其实知道 a 标签是可以写一个锚点的,也就是形如 <a href="#test"></a>,此时这个 a 标签的链接就是一个锚点,点击这个锚点就会跳转到 id 为 test 的元素,这个效果的实现方式其实很简单,比如一个网站链接为 http://www.url.com,我希望打开链接直接跳转到这个链接中 id 为 test 的元素,我就可以这样写 http://www.url.com#test,当我们打开这个网址的时候,就会直接跳转到 id 为 test 的元素。a 元素的跳转也是这么实现的,当我们点击这个 a 元素后,会在网址后面拼接上 #test,此时就会直接跳转到 id 为 test 的元素。网址中的这个 test,就是我们说的哈希,可以在 JS 中使用 location.hash 修改或者获取。

现在我们知道,这样的锚点跳转本质上是在网址中添加哈希的方式实现的,:target 这个选择器,就是选择当前网页上哈希和 id 属性值一致的元素。

css
:target {
   color: green;
}
html
<a href="#test">ee</a>
<p id="test">123</p>

f8a700f70b3d42d02a724e34ae954628_MD5

当点击 a 元素后,会跳转到 id 为 test 的元素,也就是下面的 p 元素

025948317693048bd881849820e59e6d_MD5

此时网址链接的最后会被加上哈希,也就是 #test,加上之后浏览器会自动跳转到 id 为 test 的元素,而 target 元素选中的就是 id 属性值和当前网址哈希一样元素。由于这个 p 元素的 id 为 test,当前网址的哈希也是 test,所以自然就被选中了。

有了这个选择器我们可以实现一些效果

css
:target {
   color: green;
}
html
<a href="#test">ee</a>
<a href="#test2">ee</a>
<p id="test">123</p>
<p id="test2">123</p>

99f0553c81d96940247a4fe58c4db3d6_MD5

此时当我们点击第一个 a 标签的时候,会将当前网址的哈希修改为 test,此时 :target 选中 id 为 test 的元素,当我们点击第二个 a 标签的时候,网址哈希会修改为 test2,此时 :target 会选中 id 为 test2 的元素。这样就可以实现类似选项卡切换的效果。

这个选择器有一个比较重要的意义,点击一个 a 元素会修改锚点,修改锚点会导致 :target 选择器选择的内容发生改变,从而修改其样式,这就使得我可以通过点击一个 a 元素修改另一个毫不相关的元素,这样的效果在以往的 CSS 中是没有的。

接下来看一组伪类选择器

css
:first-child{}

这个选择器用于选取第一个子元素的元素,什么意思呢,就是被选择的元素必须是其父元素的第一个子元素。

html
<div>
   <p>1</p>
   <p>2</p>
   <p>3</p>
</div>

在这个 HTML 结构下,如果使用 div:first-child,会选中整个 div 元素,这个选择器表示的就是选中 div,并且必须是其父元素的第一个子元素,如果我们要选择第一个 p 元素,需要使用 div p:first-child 这样的写法,表示 div 下面的所有 p 元素中的第一个子元素。

当我们要选择最后一个 p 元素时,可以使用 div p:last-child 来选择。

伪类选择器本质上起一个过滤的作用,先选择一批元素,然后根据一定条件进行过滤,比如我要选中第一个子元素,最后一个子元素,比如我要选择鼠标悬浮的元素,这些都是过滤作用。我们可以把伪类理解为一种状态过滤器,就是从我已选中的一些元素里,根据某些规则过滤出一些元素。

css
p:only-child{}

选中所有 p 元素中是独生子的那些 p 元素,也就是如果这个 p 元素的父元素中只有自己,自己就会被选中

html
<div>
        <p>1</p>
</div>
<div>
        <p>1</p>
        <p>1</p>
        <p>1</p>
</div>
<div>
        <p>1</p>
        <span>1</span>
</div>

以上结构只有第一个 p 会被选中,因为只有第一个 p 是其父元素的独生子。

css
p:nth-child(n){}

这个选择器可以选择第几个子元素,上面这个选择器选中的就是所有 p 元素里,是其父元素的第 n 个子元素的元素,这里举个例子来解释一下

css
p:nth-child(2){
	color:red;
}
html
<div>
        <p>1</p>
        <p>1</p>
        <p>1</p>
        <p>1</p>
</div>

这样就会选中所有 p 元素中,位于第二个子元素的 p 元素,注意,这里的下标从 1 开始。

这个 :nth-child() 括号里可以填入公式,:nth-child(2n),这个可以选中所有偶数元素,:nth-child(2n+1) 选中的是所有奇数的元素。

其实它其实是 n 从 0 开始递增,当我们写的公式为 2n 时,会选中第 2*0,2*1,2*2,2*3...,当我们写上 2n+1 时,会选中第 2*0+1,2*1+1,2*2+1,...

我们掌握了这个规律就可以写各种各样的公式,比如我要隔两个选一个,就是 3n,此时会选中 3*0,3*1,3*2,...,也就是 0,3,6,...,注意,这里的 n 是从 0 开始递增的,而不是 1。

:nth-child(0) 什么也不会选中

:nth-child(n) 是从前往后正着数,如果要从后往前倒这数那就是 :nth-last-child(n)

:nth-child() 有一个需要注意的地方,请注意这里的描述,比如 :nth-child(2) 选择的这个元素是其父元素的第二个子元素。

html
<div>
        <span>1</span>
        <p>1</p>
        <p>1</p>
        <p>1</p>
</div>

对于上面这个结构,如果使用 p:nth-child(2) 选中的就如上面的注释所示,因为这个 p 是 div 的第二个子元素。

带有 child 的这一系列选择器,都是在选择第几个孩子,什么意思呢,比如说 p:first-child,这个选择器选择的是 p 元素中位于其父元素第一个子元素的元素。p:nth-child(4) 是选中 p 元素中位于其父元素的第四个子元素的元素。在计算子元素的时候,其他元素也会计算在内。

实际上,带 child 的这几种写法并不是很常用,更常用的应该是下面这一组。

css
p:first-of-type{

}

这个选择器选择的是,位于其父元素的所有子元素中第一个 p 元素的元素。这个很绕,但其实并不难理解,这里顺便和 first-child 进行区分。

html
<div>
        <span>1</span>
        <p>1</p>
        <p>1</p>
        <p>1</p>
</div>
<div>
        <p>1</p>
        <p>1</p>
        <p>1</p>
</div>

能看到区别吗,first-child 选中的元素,一定是位于其父元素的第一个子元素,而像上面这样,选中所有 p 元素中第一次出现的 p 元素,就得用 first-of-type,这个第一次出现的 p 元素并不一定是其父元素的第一个子元素。

我们可以这样理解,first-child 是选中其父元素的第一个元素,first-of-type 是将其不同兄弟元素全部排除后,选择出第一个。所以其实 first-of-type 和 first-child 本质上的区别是,前者选出的元素不受其他元素的干扰,就是把其他元素排除在外,我是第一个。后者是收到其他元素干扰的,也就是说哪怕不排除其他元素,我也必须是位于第一个。

同样 :last-of-type 选择的就是同类元素的最后一个

:only-of-type 表示的则是只有我一个,这里和 only-child 不太一样,可以看下面的例子

html
<div>
        <span>1</span>
        <p>1</p>
        <p>1</p>
        <p>1</p>
</div>

在这个结构中,如果使用 span:only-of-type 就可以选择出那唯一一个 span,而如果使用 span:only-child 则选不出来。only-of-type 也是将其他元素排除之后,只有我一个元素,那就选出来。

同样,nth-of-type() 就是把其他元素排除之后,第几个元素,比如 p:nth-of-type(2),就是将其他元素全部排除之后,第二个 p 元素。

html
<div>
        <span>1</span>
        <p>1</p>
        <p>1</p>
        <p>1</p>
</div>

对于上面这个结构,如果我们分别用 div p:nth-child(2) 和 div p:nth-of-type(2),则会选中两个不同的元素,这也就是二者的区别,nth-child 不会排除掉其他元素,选中的元素那就是其父元素的第二个子元素。而 nth-of-type(2) 则会排除其他元素,只考虑自己,就是在其父元素的所有子元素中,所有和自己同类元素中第二个出现的元素。

同样,nth-of-last-type() 就是从后往前数,并且这里面也可以填函数,比如填 2n 表示偶数,2n+1 表示奇数

这种选择第几个的伪类选择器其实可以分为两组,一组是包含 child,一组是包含 type, type 类选择器更常用也更好用。

css
div:empty{

}

这个 :empty,会选择空元素,也就是没有任何内容的元素,也就是啥也没有。

html
<p>1</p>
<p> </p>
<p></p>
<p></p>
<p>1</p>

对于以上 HTML 结构,如果使用 p:empty 去选择,就会选中以上两个,所以我们可以得出这几个结论。

  • empty 伪类选择器会选择没有任何内容的元素
  • 注释不算做内容
  • 空格也算内容

对于接下来的几个选择器,我相信举个例子就能懂

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        input:checked {
            height: 50px;
            width: 50px;
        }
    </style>
</head>
<body>
    <label>
       这是一个选择框:<input type="checkbox">
    </label>
</body>
</html>

这个代码的效果如下所示

ffdc6215b1ad1718f3111250d34148f2_MD5

所以 :checked 可以选择处于选中状态的 checkbox。

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        input:not(:checked) {
            height: 50px;
            width: 50px;
        }
    </style>
</head>
<body>
    <label>
       这是一个选择框:<input type="checkbox">
    </label>
</body>
</html>

e4858a55d5fe68ad6f873f636c683050_MD5

我们这里使用前面学到的 not 伪类稍微修改一下,就可以反过来,给没有被处于选中状态的多选框设置样式

:checked 也可用于选择被选中的单选框(radio)

我们这里可以结合前面学到的选择器实现一个小功能

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        input:checked+span {
            color:red;
        }
    </style>
</head>
<body>
    <label>
       这是一个选择框:<input type="checkbox"> <span>test</span>
    </label>
</body>
</html>

e67d65573e126bff9cc8d64f457e3302_MD5

这样就实现了点击之后让后面的 span 标签变颜色的效果。

我们知道 input 表单元素其实是可以禁用的,使用 disabled 属性即可,CSS 3 也提供了伪类用于选中被禁用的元素。

input:disabled 用于选中被禁用的表单,input:enabled 用于选中没有被禁用的表单。

由于禁用这个东西在现在的页面中不怎么能见到,并且用法也非常简单,所以并不过多强调。

除了禁用以外,input 其实也可以设置只读。

html
<input type="text" readonly value="这是一个只读文本">

这样这个 input 就变成只读状态了,这种只读状态的文本我们也可以给他选出来,使用 input:read-only 即可。同样相对的,对于可读可写的表单,也可以使用 :read-write 选出来。

练习:手风琴效果

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        a {
            height: 20px;
            background-color: lightblue;
            width: 100px;
            line-height: 20px;
            display: block;
            color: #333;
            text-decoration: none;
        }
        p:not(:target) {
            height: 0;
        }
        p {
            height: 20px;
            width: 100px;
            overflow: hidden;
        }
    </style>
</head>
<body>
    <a href="#one">test1</a>
    <p id="one">1</p>
    <a href="#two">test2</a>
    <p id="two">2</p>
    <a href="#three">test3</a>
    <p id="three">3</p>
</body>
</html>

以笔记为镜,可以知得失