CSS基础---了解你知道的选择器

273 阅读31分钟

一、前言

之前了解了基础的CSS2.1,那么相关属性特点啥的应该有一定的了解,现在了解一下与之搭配的CSS选择器吧!!!!!!

二、选择器的优先级

(1)0 ~ 5 之分

说明: 可以理解为优先级有六个等级;这里的前四个的优先级由CSS选择器决定,后两个等级由书写形式和特定语法组成

==> 0级 <==

选择器: 通配选择器、选择符、逻辑组合伪类

注意:

  • 选择符指+、>、空格、||这四个

  • 通配选择器使用的是通配符*

  • 逻辑组合伪类有:not():is()等,它们本身不会影响CSS的优先级,只是括号里面的选择器影响

==> 1级 <==

选择器: 标签选择器

举例:

body {
    color: red;
}

==> 2级 <==

选择器: 类选择器、属性选择器、伪类

举例:

.foo {
    color: red;
}

[foo] {
    color: red;
}

:hover {
    color: red;
}

==> 3级 <==

选择器: ID选择器

说明:

#foo {
    color: red;
}

==> 4级 <==

书写形式: style内联样式

举例:

<div style="color: red">style的优先级</div>

==> 5级 <==

语法: !important

举例:

.foo {
    color: red !important;
}

注意: 这是顶层优先级,可以重置JavaScript设置的样式,所以推荐在需要将JavaScript设置的样式无效的地方使用

(2)优先级规则

==> 数值计数法 <==

说明: 每一段CSS的语句的选择器都可以对应一个具体的数值,数值越大优先级越高,其里面包含的CSS样式也会优先渲染,也就是如果发生冲突,以优先级高的为主;其次数值的具体表现为每出现一个0级选择器,优先级数值+0,出现一个1级选择器,优先级数值+1,依次类推,0 ~ 5级选择器对应的值分别为0、1、10、100、1000;看下面的例子

选择器计算值计算明细
*{ }01个0级通配选择器,优先级数值为0
div { }11个1级标签选择器,优先级数值为1
li > ol + ol { }33个1级标签选择器,2个0级选择符,优先级数值为 1 + 0 + 1 + 0 + 1
.foo { }101个2级标签选择器,优先级数值为10

注意: 这个计数是不存在进位的,也就是说10个1级选择器和一个2级选择器相比,还是2级选择器的优先级更高

技巧: 对于增加选择器的优先级可以选择重复选择器自身来进行优先级权重的增加,这样的好处在于提升了代码的可维护性

==> 后来居上 <==

解释: 当上面的方法比较不出来的时候,后渲染的选择器优先级更高,简单理解就是越在下面,优先级越高;这个规则是针对整个HTML页面的,而不是针对一个单独的css文件,

<!-- 此时foo的color就是yellow了 -->
<style>
    .foo {
        color: red;
    }
</style>
<link rel="stylesheet" href="a.css" />
<link rel="stylesheet" href="b.css" />
/* a.css */
.foo {
    color: blue;
}
/* b.css */
.foo {
    color: yellow;
}

三、选择器命名

(1)区分大小写

说明: 在HTML中,标签和属性是不区分大小写的,但是属性值区分大小写,于是在CSS中也得到应用:标签、属性选择器区分大小写,而类选择器和ID选择器由于其本质是属性值,因此区分大小写

P {
    color: red;
}

[Class] {
    color: yellow;
}

/* 设置没有效果 */
.container {
    color: white;
}
<p class="CONTENT">颜色是啥</p>

解决办法: 如果需要忽略属性值的大小写带来的影响的话,可以使用[class~="类名" i]的形式来定义,这样不管你怎么大小写都不会有影响

/* 以上面的举例 */
[class~="content" i] {
    color: red;
}

(2)命名的合法性

说明: 这里说一下类选择器和ID选择器命名合法性

==> 误区 <==

举例: 类名选择器和ID选择器不能够以数字开头

/* 这种写法确实不行 */
.1-foo {}

/* 不是不能以数字开头,而是不能直接写数字,将其转义一下就好 */
/* 注意:31后面是有一个空格的 */
.\31 -foo {}

/* 前面补四个0,数字31后面就不用空格了 */
.\31-foo {}

注意: 如果选择器中存在父子关系,那么就需要两个空格;由于CSS压缩工具会将空格去除,因此建议使用非空格写法,就是补0

==> 规范与字符合法性 <==

规范:

image.png

解释: 从图可以看出命名分左右两部分,它们都能使用a~zA~Z_和非ASCLL字符,只是右边比左边多0-9-而已;在图下面存在escape转义的字样,也就是对于其它没有出现的字符,将其转义重新编码之后也是支持字符的类型,因此可以得到以下特性

  • 不合法的ASCLL字符,像!、"、#、$、%、&、'、(、)、*、+、-、.、/、:、;、<、>、=、?、@、[、]、\、^、{、}、|、~这些都可以按照非空格写法进行转义,不过这显得很麻烦,有种更简单的方法就是直接使用\就可以了,只需要注意IE7下的\:是存在问题的
  • 可以直接使用中文字符、中文标点、emoji表情来做类名
/* 直接使用\ */
.\+foo {}

/* 中文 */
.我是 {
    color: red;
}

/* 标点符号 */
.。 {
    color: red;
}

注意: 图中还存在---,前者含义是如果只有一个-,那么它的后面需要跟其它字符;后者含义是--可以单独存在,后面可以不跟其它字符

.-- {}

.-a-b- {}

(3)选择器的命名

说明: 这里说一下单命名组合命名这两种方式,它们各有优缺点如下;根据特点,推荐像多人合作、长期维护、不引入第三方的css作为公共样式的项目可以使用单命名,其它的推荐组合命名;这里只是推荐,当然你一直组合命名也可以!

  • 单命名: 优点在于字符少,写起来速度很快,但是容易出现命名冲突,毕竟简单的东西写多了就复杂起来了

  • 组合命名: 优点是不容易出现命名冲突,但是与短命名相比,写法要复杂一些,所以其缺点就是繁琐

/* 单命名 */
.title {}
.box {}

/* 组合命名:一般都需要一个前缀,这也是为什么出现命名冲突的情况变少的原因 */
/* 这里CS的原因是CSS选择器英文取前面两个字母组成的,有点语义化的味道 */
.cs-title {}
.cs-box {}

注意: 命名的时候排除拼音的写法,有点看起来傻傻的,在一些人看来你可能是一个小白,而且汉字的多音字很多,很容易就让人懵了

/* 这看见了别人不得拼一下才知道是啥意思 */
.hanbaobao{}

(4)需要注意的点

==> 不要使用ID选择器 <==

说明: 这是因为其优先级很高的原因,在重置样式的时候可能会出现写多个类名还是无效的问题,如果非要使用的话可以尝试属性选择器像[id="id值"]的形式使用

==> 不要嵌套选择器 <==

说明: 它存在三个缺点:渲染性能很差、优先级混乱、布局脆弱,正确做法是使用纯类名选择器,不然你可能在玩火

<== 渲染性能差 ==>

说明: 这个体现在两个方面,一个是使用过多的标签选择器,一个是过深的选择器嵌套,在渲染性能上,它是差于ID选择器和类选择器的,因此在常用的选择器中它属于性能低效的,如果页面上这种选择器很多,当然性能就存在一定的问题;对于嵌套而言,每一次嵌套就会多一层计算,因此性能低也就不言而喻了

<== 优先级混乱 ==>

说明: 选择器的优先级默认情况是会保持最低的,一旦开始嵌套,其优先级规则就变得很复杂,当重置样式的时候可能你就会发现你写的样式不管用;你可能想着使用同样的选择器写在后面去重置、使用更深的选择器嵌套去重置、使用ID选择器、使用!important等这些方法去重置你的样式,可能效果你能实现,但是下次呢,还这样写嘛,能一直保持效果实现嘛,这个有种恶性循环的感觉

<== 布局脆弱 ==>

说明: 这个主要体现在标签选择器上,它会把某一块的内容定死,比如这里你在css中使用了span,然后当html中这个标签改掉的时候,比如改成div,那么你这个样式就失效掉了,这就体现其脆弱的特性

(5)面向属性的命名

.dn {
    display: block
}

说明: 比如上面这种,可以理解为以属性和属性值的首字母作为类名,这个一般使用于特殊场景,比如在某一个角落里面有一张图片,它的display的值表现为block,这段css在其它地方完全用不到,此时你会去认真思考给它一个语义化的类名吗,这显然是画蛇添足,自己找麻烦,因此你选择标签选择器,恭喜你,你总有一天会后悔的,正确的是应该使用上面的dn,最起码这个不会消耗你思考的时间,也不会存在性能上面的问题!

四、CSS选择符

(1)后代选择符(空格)

举例: 下面HTML中文字的颜色是什么?为什么?

疑惑: 这里有地方没理解清楚,有理解的伙伴们可以分享一下

<div class="linghtblue">
    <div class="darkblue">
        <!-- darkblue -->
        <p>颜色是?</p>
    </div>
</div>

<div class="darkblue">
    <div class="linghtblue">
        <!-- darkblue -->
        <p>颜色是?</p>
    </div>
</div>
.lightblue {
    color: lightblue;
}

.darkblue {
    color: darkblue;
}

image.png

解释: 当包含后代选择器的时候,选择器的优先级与祖先元素的DOM层级没有任何的关系,需要看落地元素的优先级,落地元素就是最后一个后代选择器所代表的元素,优先级是看其嵌套关系,上面例子中落地元素就是p元素,然后两个p元素是彼此分离,没有嵌套,因此DOM层级没有先后,是平行关系,这里的嵌套我是懵的状态,我试过在p元素前后里面外面嵌套,还是没找到答案,这里明白的伙伴们可以回复一下;当落地元素的优先级是相等状态,就比较其前面的选择器的优先级,优先级高者优先,如果还是比较不出来,就后来居上原则,因此上面文字都是深蓝色

(2)子选择符(>)

说明: 它与后代选择器的区别在于它只能影响一代子元素,后代选择器能够影响所有子元素,因此在同样的选择器下它能选择到的元素会更少一些,这就是它的一大优点,一般用这个控制一代元素的特点做精细控制,比如父子元素只希望父元素能够有某些特点,而子元素不需要的这种操作来避免冲突的发生,但是一旦使用了子选择器,其层级关系就被锁定,当层级发生改变的时候,样式也就会失效,因此它也是一把双刃剑

<ol>
    <li>第一代</li>
    <li>
        <ul>
            <li>第一代的第一代</li>
            <li>第一代的第一代</li>
            <li>第一代的第一代</li>
        </ul>
    </li>
    <li>第一代</li>
</ol>
ol li {
    color: red;
    text-decoration: underline;
}

ol > li {
    color: black;
    text-decoration: underline wavy;
}

image.png

(3)相邻兄弟选择器(+)

==> 基本使用 <==

<ol>
    <li>1</li>
    <li class="cs-li">2</li>
    <li>3</li>
    <li>4</li>
</ol>
/* 表示选择.cs-li后面一个相邻且标签是li的元素 */
.cs-li + li {
    color: red;
}

image.png

注意: 如果写成.cs-li + .cs-li的话,根据特点只会选择后一个.cs-li的元素,想象一下,如果同一层级都是.cs-li这样的元素,每次都选择后一个,你就会发现最开始的哪一个元素就不会选择,也就完成了选择除了第一个以外的元素的效果了

==> 特点 <==

特点: 会忽略文本节点和注释节点,只认元素节点。

<h4>文本节点和注释节点</h4>
哈哈哈哈
<!-- 注释的内容 -->
<p>内容</p>
h4 + p {
    color: red;
}

image.png

==> 高级选择器技术核心 <==

说明: 一般结合伪类来实现很多实用的交互效果,也就是不使用JS实现的效果

举例: 选中输入框的时候,让跟在后面的文字显示出来

.cs-tips {
    color: gray;
    margin-left: 15px;
    position: absolute;
    visibility: hidden;
}

:focus + .cs-tips {
    visibility: visible;
}
用户名:
<input type="text" />
<span class="cs-tips">不超过10个字符</span>

image.png

(4)随后兄弟选择器(~)

说明: 它和相邻兄弟选择器的区别在于前者是匹配所有元素,后者是匹配一个元素

.cs-h ~ .cs-li {
    color: skyblue;
    text-decoration: underline;
}

.cs-h + .cs-li {
    text-decoration: underline wavy;
}
<p class="cs-li">1</p>
<h4 class="cs-h">间隔</h4>
<p class="cs-li">2</p>
<p class="cs-li">2</p>

image.png

==> 为啥没有前面兄弟选择器 <==

说明: 既然能够选择后面,为啥不能选择前面呢,可能是受限于DOM渲染规则,浏览器解析HTML文档都是从前到后、从上到下开始的,如果浏览器支持前面兄弟选择器,那么也就是需要先解析后面的元素,如果后面的元素没加载完,对前面元素的加载也会有影响,那么网页的速度就会减慢很多,同时浏览器会出现长时间的白班,在体验上会贼差,因此不存在这样的选择器

==> 实现类似前面兄弟选择器的效果 <==

说明: 有时候存在通过后面的元素去控制前面元素这样的效果,其实可以通过随后兄弟选择器来实现类似的效果,它说选择后面元素中的后面是说代码中的后面,而不是视觉上的,因此可以通过把前面元素放在代码后面,然后在视觉上展示在前面就行了;DOM位置和视觉位置不一样的实现方法很多,比如说最简单的浮动、定位就可以,比如下面这种:

<div class="cs-absolute">
    <input type="text" class="cs-input" />
    <label class="cs-label">用户名</label>
</div>
.cs-absolute {
    width: 264px;
    position: relative;
}

.cs-input {
    width: 200px;
    margin-left: 64px;
}

.cs-label {
    position: absolute;
    left: 0;
    top: 0;
}

/* + 和 ~ 都可以*/
:focus + .cs-label {
    color: red;
    text-shadow: 0 0 1px;
}

image.png

五、元素选择器

说明: 元素选择器有两类,一类是标签选择器,一类是通配符选择器

(1)级联语法

说明: 也就是使用divdiv这样的写法,中间没有空格的,其它选择器也有这样的语法,只不过区别在于两点,一是元素选择器不能重复自身,二是元素选择器需要写在前面

/* 无效 */
divdiv {}

/* 无效 */
[type="radio"]input {}

注意: 那么元素选择器就不能通过重复自身来提高选择器优先级了,这个方法在前面提到过,因此可以尝试下面这两种方式

/* 标准的HTML页面元素都存在body元素和html元素 */
body div {}

/* 借助:not()伪类,括号里面是不同的标签名就可以 */
div:not(a) {}

(2)小知识

  • 标签选择器不区分大小写,但是默认习惯还是全小写

  • 在使用属性选择器的时候前面的标签选择器推荐省略不写,这样选择器的优先级和类选择器的优先级保持一致,可维护性得到提高

  • 现代浏览器中可以使用自定义元素的标签来控制自定义元素的样式

  • 通配符选择器与其它选择器级联使用的时候,可以省略不写

/* 大写也是可以的 */
IMG {}

/* 这里的input可以不写,因为radio类型的单选框一定是input标签 */
input[type="radio"] {}
[type="radio"] {}

/* 比如有一个x-element的元素 */
x-element {}

/* 通配符与其它选择器级联时,*可以省略 */
*.warning {} <=> .warning {}

六、属性选择器

说明: 属性选择器又叫属性值匹配选择器,在正式文档中,类选择器和ID选择器都属于属性选择器,因为本质上类选择器是HTML元素的class的属性值,而ID选择器是HTML元素中id的属性值

(1)类选择器和ID选择器的区别

==> 语法不同 <==

/* 类选择器前面是点号 */
.class {}

/* id选择器前面是井号 */
#id {}

==> 优先级不同 <==

说明: ID选择器的优先级比类选择器的优先级高一个等级,因此不到万不得已不要使用,避免带来维护成本

==> 唯一性和可重复性 <==

  • 对类选择器: 一个HTML元素的class属性中可以放多个值;一个类选择器中的样式可以控制多个HTML元素

  • 对ID选择器: 一个id属性中只能存放一个值,否则id就会失效;根据默认的规定,不能存在ID值相同的元素,这也是id值的唯一性

<!-- class中可以存在多个值 -->
<div class="cs-type cs-button"></div>

<!-- 这是一个样式可以控制多个元素 -->
<div class="cs-type"></div>
<button class="cs-type"></button>
<!-- 这样写id的样式会失效 -->
<div id="cs-type cs-button"></div>

<!-- 不推荐一个id值控制多个元素,因为要保证id值的唯一性 -->
<div id="cs-type"></div>
<button id="cs-type"></button>

(2)属性值直接匹配选择器

==> [attr] <==

说明: 这个表示只要元素包含指定的属性就匹配,一般适用于一些HTML布尔属性,比如disabled、checked等

<!-- 正常的input框 -->
<input type="text" />

<!-- 表示禁用的input框 -->
<input disabled />
<input disabled="" />
<input disabled="disabled" />
<input disabled="true" />
<input disabled="false" />
/* 只有带disabled属性的才有红色边框色 */
[disabled] {
    border: 1px solid red;
}

image.png

注意: 对于checked属性而言,其存在兼容性问题;其次表单控件元素在checked状态变化的时候不会同步修改checked的属性值,因此不建议在项目中使用

==> [attr="val"] <==

说明: 这个需要属性名和属性值都相等才能匹配上,属性值的引号不分单双;也可以省略,但是在省略的基础上,如果属性值中存在空格,那么就必须转义,否则还是使用引号为好

/* 匹配单选框 */
[type="radio"] {}
    
/* 引号的单双是无所谓的 */    
[type='radio'] {}

/* 引号可以省略,如果出现空格需转义,否则还是用引号 */
[class="cs-type cs-radio"] {}
[class=cs-type\0020cs-radio] {}

注意: type为eamil、url、number、tel和range的时候,在IE9以下的浏览器中,会被替换成text,因此导致属性选择器失效,从而产生样式问题

==> [attr~="val"] <==

说明: 这个是属性值单词完全匹配选择器,也就是匹配一个属性值字符串中的一个单词,这个单词需要是完整的,完整性的判断是看单词是否被空格分隔

/* 假设匹配[class~="val"]的元素 */
[class~="val"] {}
<!-- 能够匹配 -->
<div class="val">2</div>

<!-- 能够匹配 -->
<div class="val     ue">2</div>

<!-- 不能匹配,因为不完整 -->
<div class="value">2</div>

<!-- 不能匹配,没有使用空格分隔 -->
<div class="val-ue">2</div>

注意:

/* 空字符串匹配不到任何元素 */
[class~=""] {}

/* 可以匹配中文 */
[class~=爽] {}

==> [attr|="val"] <==

说明: 这个表示属性值起始片段完全匹配选择器,以标题为例,表示具有attr属性的元素,值要么是val,要么是val-开头,否则就匹配不上,一般在选择语言的场景使用的比较多

<!-- 能够匹配 -->
<div attr="val"></div>
<div attr="val-ue"></div>
<div attr="val-ue bar"></div>

<!-- 不能匹配 -->
<div attr="bar val-ue"></div>
<div attr="value"></div>
<div attr="bar val"></div>

(3)属性值正则匹配选择器

说明: 此时匹配的是字符,而不是单词了

注意: 如果需要忽略大小写的问题,可以像前面那样使用i来完成,比如[attr^="val" i]

==> [attr^="val"] <==

说明: 这个表示只要你元素的attr属性的开头是val,那么就能匹配上;一般用来判断a元素链接地址类型或者显示小图标

<!-- 能够匹配 -->
<div attr="val"></div>
<div attr="val-ue"></div>
<div attr="value"></div>

<!-- 不能匹配 -->
<div attr="text val"></div>

<!-- 空字符串匹配不到元素 -->
<div attr=""></div>

<!-- 匹配中文,需要注意的是匹配中文的时候,如果没有特殊字符,引号可以省略 -->
<div attr="我"></div>

==> [attr$="val"] <==

说明: 这个跟上面哪一个的区别在于只要末尾字符存在val,就匹配,其它的跟上面那个是一样的

==> [attr*="val"] <==

说明: 这个表示只要attr属性中存在字符val就匹配,一般在匹配链接元素是否为外网地址的时候用的比较多

七、用户行为伪类

(1):hover

说明: 在鼠标移入元素的时候触发,一般用于桌面端网页,不过移动端也能够触发,但是消失并不敏捷,会带来一种奇怪的体验;其次,触发是一瞬间的事情,如果希望有过渡效果,一般使用css的transition属性,值得注意的是,这个属性对display没有过渡的效果,因此需要使用visibility属性来完成

举例: 鼠标经过图片链接的时候图片一直保持显示

/* 使用padding填充当文字与图片间存在间隙的时候图片还是能够显示 */
a {
    padding-right: 20px;
}

img {
    visibility: hidden;
    transition: visibility 0.2s;
    position: absolute;
    z-index: 1;
}

a:hover + img,
img:hover {
    visibility: visible;
}
<a href>图片链接</a>
<img src="D:1.jpg" />

image.png

注意: 所有鼠标经过按钮,然后显示下拉列表的交互效果,都要同时保证点击行为也能够控制下拉列表的显示和隐藏,也就是需要使用CSS和JS来一起控制

(2):active

说明: 这个伪类用来设置元素激活状态的样式,当点击按下的时候会触发伪类样式,当点击抬起的时候会取消伪类样式;它支持所有的HTML元素,包括自定义元素。

作用: 主要用于反馈点击交互,让用户知道它的点击是有效果的,对于按钮和链接元素而言是不可少的东西,不过这个伪类一般在移动端使用的很多,毕竟桌面端可以使用:hover来实现;在移动端上,可以使用box-shadow或者linear-gradient来完成反馈的效果

缺点:

  • IE浏览器下,这个伪类样式的应用是无法冒泡

  • IE浏览器下,html、body等元素应用这个伪类设置背景色后,是无法复原的,也就是当点击的时候背景色会改变,松开后背景色是变不回去的

  • 键盘访问是无法触发伪类效果的

(3):focus

作用: 匹配当前聚焦的元素

作用元素:

  • 非disabled状态的表单元素,像input、select、button等

  • 包含href属性的a元素

  • area元素,但是生效的css属性有限

  • HTML5中的summary元素

  • 如果普通元素也想应用伪类,需要具有contenteditable属性或者tabindex属性;tabindex是为了使元素能够被键盘的tab键聚焦,对于普通元素而言,tableindex是不能使用自然数的,如果希望元素既能够被tab键索引,又能被点击的时候触发伪类样式,可以将值设置为0,如果只想元素在点击的时候触发伪类样式,则需要将值设置为-1

注意: 一个页面最多只能有一个元素响应:focus伪类;其次在模拟按钮效果的时候一般使用label标签,而不是div或者是span,因为这两个标签是没有button那样默认的行为的,因此增加了模拟成本;在模拟按钮效果的时候,一般需要隐藏之前原生的按钮,推荐使用opacity或者clip进行隐藏、裁剪,因为键盘的tab键对visibilitydisplay的元素的可访问性是0

(4):focus-within

说明: 这个跟:focus很像,只不过:focus-within能够当其子元素处于聚焦的时候会触发其父元素的聚焦样式,也就是子元素的状态能够影响父元素的样式

举例: 实现无障碍访问的下拉列表

.cs-bar {
    background-color: #e3e4e5;
    color: #888;
    padding-left: 40px;
}

.cs-details {
    display: inline-block;
    text-align: left;
}

.cs-summary {
    display: inline-block;
    padding: 5px 28px;
    text-indent: -15px;
    user-select: none;
    position: relative;
    z-index: 1;
}

.cs-summary::after {
    content: "";
    position: absolute;
    width: 12px;
    height: 12px;
    margin: 4px 0 0 0.5ch;
    background: url(./icon-arrow.svg) no-repeat;
    background-size: 100% 100%;
    transition: transform 0.2s;
}

.cs-details:focus-within .cs-summary,
.cs-summary:hover {
    background-color: #fff;
    box-shadow: inset 1px 0 #ddd, inset -1px 0 #ddd;
}

.cs-details:focus-within .cs-summary::after {
    transform: rotate(180deg);
}

.cs-datalist {
    display: none;
    position: absolute;
    min-width: 100px;
    border: 1px solid #ddd;
    background-color: #fff;
    margin-top: -1px;
}

.cs-details:focus-within .cs-datalist {
    display: block;
}

.cs-datalist-a {
    display: block;
    padding: 5px 10px;
    transition: background-color 0.2s, color 0.2s;
    color: inherit;
}

.cs-datalist-a:hover {
    background-color: #f5f5f5;
}

.cs-datalist-a:active {
    background-color: #f0f0f0;
    color: #555;
}

.cs-datalist-sup {
    position: absolute;
    color: #cd0000;
    font-size: 12px;
    margin-top: -0.25em;
    margin-left: 2px;
}
<div class="cs-bar">
    <div class="cs-details">
        <a href="javascript:" class="cs-summary">我的消息</a>
        <div class="cs-datalist">
            <a href class="cs-datalist-a">
                我的回答<sup class="cs-datalist-sup">12</sup>
            </a>
            <a href class="cs-datalist-a">我的私信</a>
            <a href class="cs-datalist-a">
                未评价订单<sup class="cs-datalist-sup">2</sup>
            </a>
            <a href class="cs-datalist-a">我的关注</a>
        </div>
    </div>
</div>

image.png

(5):focus-visible

作用:使用键盘让元素聚焦的时候会触发,因此它可以让使用者知道元素的聚焦状态是通过鼠标点击制造的还是键盘的tab键制造的

八、URL定位伪类

(1):link

作用: 匹配页面上存在href属性但是没有被访问过的a标签

说明: 也就是它能够设置默认的链接样式;因此它需要放在最前面,避免出现样式优先级的问题,放置的顺序遵循:link -> :visited -> :hover -> :active;它最大的优势在于可以确定链接的真假,毕竟它显示的依据是href,因此按钮的禁用和非禁用就很好控制了

/* 链接色默认为红色 */
a:link {
    color: red;
}

缺点:

  • 对于已经访问过的链接样式会失效
  • 只能作用于a标签

注意: 由于a链接的效果和:link的效果是一样的,因此:link就使用的比较少了,不过不要使用a标签选择器,因为这样不符合语义,推荐使用属性选择器;为了避免上面的缺点,可以使用:any-link伪类来代替:link

/* 设置默认的a链接的样式 */
[href] {}

/* 区分a链接是否禁用 */
.cs-button:not([href]) {}

(2):visited

作用: 用于定义已经访问过的a链接的样式

特点:

  • 有限的属性: 它支持的css属性很少,只有color、background-color、border-color、border-bottom-color、border-left-color、border-right-color、border-top-color-column-rule-color、outline-color

  • 没有半透明: 虽然在访问过的链接样式中可以设置透明色,但是实际展现出来的要么是全色,要么是全透明色

  • 颜色重置: 假设默认的链接都使用默认的颜色,那么使用:visited去凭空增加颜色的时候颜色是没用的,假设默认的字体是黑色,那么访问后的链接样式我就可以去更改这个字体颜色了,其它颜色样式依旧更改不了

  • 无法获取: 就是使用:visited设置的样式是无法通过JS来获取的

(3):target

==> :target与锚点 <==

锚点: 假设页面的URL地址为file:///C:/Users1.html#cs-first 那么#cs-first就是锚点,cs-first就是锚点值,这个值可以通过location.hash进行获取或者修改;锚点值一般和页面元素的id值进行匹配;当触发锚点定位的时候,浏览器默认的行为是滚动,滚动过后进行:target的伪类匹配

<ul>
    <li id="cs-first">第一行</li>
    <li id="cs-another">第二行</li>
    <li id="cs-last">第三行</li>
</ul>
li:target {
    color: skyblue;
}

image.png

注意:

  • 锚点定位一般不使用a标签的name属性进行匹配,因为存在IE浏览器的兼容性问题

  • 发生锚点定位的时候如果存在多个id值与锚点值相同的情况,以第一个为准,不过id值重复也会产生兼容性问题并且不符合语义,因此不要这样写

  • 当匹配的锚点元素是display:none的时候,浏览器不会触发滚动,但是:target还是会照样进行匹配

<ul>
    <li id="cs-first" hidden>第一行</li>
    <li id="cs-another">第二行</li>
    <li id="cs-last">第三行</li>
</ul>

image.png

==> 交互效果 <==

说明: 主要是把锚链元素放在最前面,然后通过兄弟选择符~来控制对应元素的变化,如果纯用css来实现推荐在项目要求不高的地方使用

<== 展开收起效果 ==>

/* 默认更多和收起按钮都是隐藏的 */
.cs-more-p,
[data-open="false"] {
    display: none;
}

/* 匹配后更多按钮隐藏 */
:target ~ [data-open="true"] {
    display: none;
}

/* 匹配后将展示的内容和收起按钮显示出来 */
:target ~ .cs-more-p,
:target ~ [data-open="false"] {
    display: block;
}
<div style="width: 400px">
    文章内容,文章内容,文章内容,文章内容,文章内容,文章内容,文章内容……
    <div id="articleMore" hidden></div>
    
    <a href="#articleMore" class="cs-button" data-open="true">
        阅读更多
    </a>
    
    <p class="cs-more-p">
        更多文章内容,更多文章内容,更多文章内容,更多文章内容。
    </p>
    
    <a href="##" class="cs-button" data-open="false">
        收起
    </a>
</div>

image.png

<== 选项卡效果 ==>

/* 默认选项卡按钮样式 */
.cs-tab-li {
    display: inline-block;
    background-color: #f0f0f0;
    color: #333;
    padding: 5px 10px;
}

/* 选中后选项卡按钮样式 */
.cs-tab-anchor-2:not(:target)
+ :not(:target)
~ .cs-tab
.cs-tab-li:first-child,
.cs-tab-anchor-2:target ~ .cs-tab .cs-tab-li:nth-of-type(2),
.cs-tab-anchor-3:target ~ .cs-tab .cs-tab-li:nth-of-type(3) {
    background-color: deepskyblue;
    color: #fff;
}

/*  默认选项面板样式*/
.cs-panel-li {
    display: none;
    padding: 20px;
    border: 1px solid #ccc;
}

/* 选中的选项面板显示 */
.cs-tab-anchor-2:not(:target)
+ :not(:target)
~ .cs-panel
.cs-panel-li:first-child,
.cs-tab-anchor-2:target ~ .cs-panel .cs-panel-li:nth-of-type(2),
.cs-tab-anchor-3:target ~ .cs-panel .cs-panel-li:nth-of-type(3) {
    display: block;
}
<div class="cs-tab-x">
    <i id="tabPanel2" class="cs-tab-anchor-2" hidden></i>
    <i id="tabPanel3" class="cs-tab-anchor-3" hidden></i>
    
    <div class="cs-tab">
        <a href="#tabPanel1" class="cs-tab-li">选项卡1</a>
        <a href="#tabPanel2" class="cs-tab-li">选项卡2</a>
        <a href="#tabPanel3" class="cs-tab-li">选项卡3</a>
    </div>
    
    <div class="cs-panel">
        <div class="cs-panel-li">面板内容1</div>
        <div class="cs-panel-li">面板内容2</div>
        <div class="cs-panel-li">面板内容3</div>
    </div>
</div>

image.png

九、输入伪类

(1)输入控件状态

==> :disabled <==

说明: 最简单的用法就是实现禁用状态的输入框;用的最多的地方是在使用原生按钮制作禁用按钮了

<input type="text" disabled />
/* 这两种写法都可以,第二种的兼容性更好 */
:disabled {
    border: 1px solid lightgray;
    background: #f0f0f3;
}

[disabled] {
    border: 1px solid lightgray;
    background: #f0f0f3;
}

image.png

==> :enabled <==

说明: 它和:disabled是对立作用的伪类,也就是如果同时作用在一个元素上面,那么只能有一个伪类生效。大部分情况是这样,只有a元素不是,在谷歌浏览器下,a元素无法匹配:disabled,只对:enabled生效,因此不要单独使用这个伪类,否则存在兼容性问题。最后,表单元素默认就是enabled状态,不需要额外的:enabled进行匹配,因此这个伪类在css中一般用来区分IE浏览器是IE8还是IE9+

<div style="width: 200px">
    <input type="text" disabled />
    <!-- readonly状态也认为是:enabled -->
    <input type="text" readonly />
    <input type="text" />
</div>
:disabled {
    border: 1px solid lightgray;
    background: #f0f0f3;
}

:enabled {
    border: 1px solid deepskyblue;
    background: lightskyblue;
}

image.png

注意:

  • 对于select元素,无论是自身还是子元素option,都能匹配:enabled和:disabled

  • IE浏览器下,fieldset元素不支持上面两个伪类,因此如果需要使用fieldset元素一次性禁用所有表单元素,就不能通过:disabled来识别,应当使用fieldset[disabled]选择器进行匹配

  • contenteditable="true"不能匹配:enabled

  • tableindex属性的元素也不能匹配:enabled

  • 元素设置display:nonevisibility:hidden依然能够匹配上面两个伪类

==> :read-only 和 :read-write <==

说明: 分别用于匹配输入框元素是只读还是可读可写,它们只作用于inputtextarea两个元素

注意: 这两个伪类IE浏览器不支持,因此只能在移动端、内部项目、实验项目中使用

==> :placehodlder-shown <==

说明: 这个伪类只使用于input元素和textarea元素

<== 基本使用 ==>

作用: 当输入框的placeholder属性中的内容展示的时候匹配该输入框

<input placeholder="现在只是占位符的样式" />
input {
    outline: none;
    border: 2px solid red;
}

input:placeholder-shown {
    border: 2px solid pink;
}

image.png

image.png

<== MD风格占位符 ==>

说明: 这里说的是Material Design风格,也就是输入框的占位符在输入框聚焦的时候以动画的形式移动到左上角去

做法: 先让默认的占位符效果隐藏,之后用自己的占位符元素去替换默认占位符的位置,此时就可以使用定位完成,最后在输入框聚焦以及占位符不现实的时候对自己的占位符元素进行重新定位就可以了

<h4>填充风格</h4>
<div class="input-fill-x">
    <input class="input-control input-fill" placeholder="邮箱" />
    <label class="input-label">邮箱</label>
</div>

<h4>轮廓风格</h4>
<div class="input-outline-x">
    <input class="input-control input-outline" placeholder="邮箱" />
    <label class="input-label">邮箱</label>
</div>

<h4>文本域</h4>
<div class="textarea-outline-x">
    <textarea
        class="input-control textarea-outline"
        cols="25"
        rows="3"
        placeholder="评论"
    ></textarea>
    <label class="input-label">评论</label>
</div>
.input-fill-x,
.input-outline-x,
.textarea-outline-x {
    width: -webkit-fit-content;
    width: -moz-fit-content;
    width: fit-content;
    position: relative;
}

.input-fill-x {
    border-bottom: 1px solid #d0d0d5;
}

.input-fill-x::after {
    content: "";
    position: absolute;
    border-bottom: 2px solid #2486ff;
    left: 0;
    right: 0;
    bottom: -1px;
    transform: scaleX(0);
    transition: transform 0.25s;
}

.input-fill-x:focus-within::after {
    transform: scaleX(1);
}

.input-control {
    margin: 0;
    font-size: 16px;
    line-height: 1.5;
    outline: none;
}

.input-fill {
    padding: 20px 16px 6px;
    border: 1px solid transparent;
    background: #f5f5f5;
}

.input-outline,
.textarea-outline {
    padding: 13px 16px 13px;
    border: 1px solid #d0d0d5;
    border-radius: 4px;
    transition: border-color 0.25s;
}

.input-outline:focus,
.textarea-outline:focus {
    border-color: #2486ff;
}

/* 默认placeholder颜色透明不可见 */
.input-control:placeholder-shown::placeholder {
    color: transparent;
}

.input-label {
    position: absolute;
    font-size: 16px;
    line-height: 1.5;
    left: 16px;
    top: 14px;
    color: #a2a9b6;
    padding: 0 2px;
    transform-origin: 0 0;
    pointer-events: none;
    transition: all 0.25s;
}

/* 线框样式label定位 */
.input-control:not(:placeholder-shown) ~ .input-label,
.input-control:focus ~ .input-label {
    color: #2486ff;
    transform: scale(0.75) translate(-2px, -32px);
}

/* 填充样式下label定位 */
.input-fill:not(:placeholder-shown) ~ .input-label,
.input-fill:focus ~ .input-label {
    transform: scale(0.75) translateY(-14px);
}

/* 线框交互下有个白色背景 */
.input-outline ~ .input-label,
.textarea-outline ~ .input-label {
    background-color: #fff;
}

image.png

<== 空值判断 ==>

方法: 就是利用占位符判断输入框里面是否存在内容

<input placeholder="" />
<small></small>
input:placeholder-shown + small::before {
    content: "没有输入内容";
    color: red;
    font-size: 87.5%;
}

image.png

image.png

==> :default <==

说明: 会匹配默认选中的那个元素,给它设置样式,由于它匹配不受之后的checked属性值变化的影响,因此其实用价值在于推荐标记

注意: 这个伪类只能使用在表单元素上面,还有一个条件就是如果select元素使用,那么它的子元素必须存在selected属性,如果是input元素,那么必须存在checked属性

请选择支付方式:
<p>
    <input type="radio" name="pay" id="pay0" value="0" />
    <label for="pay0">支付宝</label>
</p>
<p>
    <input type="radio" name="pay" id="pay1" value="1" checked />
    <label for="pay1">微信</label>
</p>
<p>
    <input type="radio" name="pay" id="pay2" value="2" />
    <label for="pay2">银行卡</label>
</p>
input:default + label::after {
    content: "(推荐)";
}

image.png

(2)输入值状态

==> :checked <==

<== 基本使用 ==>

/* 这两个的作用效果是一致的 */
input:checked {
    box-shadow: 0 0 0 2px red;
}

input[checked] {
    box-shadow: 0 0 0 2px red;
}
<input type="checkbox" />
<input type="checkbox" checked />

image.png

:checked 和 [checked]的区别

  • :checked只能匹配标准的表单元素,不能匹配其它的普通元素;但是[checked]能够匹配任何元素

  • [checked]属性的变化并非实时的,也就是使用这个属性选择器控制单复选框的样式会出现匹配不准确的情况

  • :checked能够正确的从祖先元素那里继承过来的状态

<== 展开收起效果(不推荐) ==>

方法: 将label元素与隐藏的复选框链接起来,之后跟:target的实现是一致的

<div style="width: 400px">
    文章内容,文章内容,文章内容,文章内容,文章内容,文章内容,文章内容……
    <input type="checkbox" id="articleMore" />
    <label class="cs-button" for="articleMore" data-open="true"
    >阅读更多</label
    >
    <p class="cs-more-p">
    更多文章内容,更多文章内容,更多文章内容,更多文章内容。
    </p>
    <label class="cs-button" for="articleMore" data-open="false">收起</label>
</div>
[type="checkbox"] {
    position: absolute;
    clip: rect(0 0 0 0);
}

.cs-button {
    display: block;
    text-align: center;
    background-color: deepskyblue;
    color: #fff;
    padding: 5px;
    margin-top: 1em;
    cursor: pointer;
}

.cs-more-p,
[data-open="false"] {
    display: none;
}

:checked ~ [data-open="true"] {
    display: none;
}

:checked ~ .cs-more-p,
:checked ~ [data-open="false"] {
    display: block;
}

image.png

<== 选项卡效果(不推荐) ==>

说明: 将几个隐藏的input元素的id属性与选项卡按钮label的for属性进行关联,这样点击按钮的时候就可以触发单选框元素的选中行为,从而实现相应的伪类匹配

.cs-tab-li {
    display: inline-block;
    background-color: #f0f0f0;
    color: #333;
    padding: 5px 10px;
}

:first-child:checked ~ .cs-tab .cs-tab-li:first-child,
:checked + input + .cs-tab .cs-tab-li:nth-of-type(2),
:checked + .cs-tab .cs-tab-li:nth-of-type(3) {
    background-color: deepskyblue;
    color: #fff;
}

.cs-panel-li {
    display: none;
    padding: 20px;
    border: 1px solid #ccc;
}

:first-child:checked ~ .cs-panel .cs-panel-li:first-child,
:nth-of-type(2):checked ~ .cs-panel .cs-panel-li:nth-of-type(2),
:nth-of-type(3):checked ~ .cs-panel .cs-panel-li:nth-of-type(3) {
    display: block;
}
<div style="width: 300px">
    <div class="cs-tab-x">
        <input id="tabPanel1" type="radio" name="tab" checked hidden />
        <input id="tabPanel2" type="radio" name="tab" hidden />
        <input id="tabPanel3" type="radio" name="tab" hidden />
        <div class="cs-tab">
            <label class="cs-tab-li" for="tabPanel1">选项卡1</label>
            <label class="cs-tab-li" for="tabPanel2">选项卡2</label>
            <label class="cs-tab-li" for="tabPanel3">选项卡3</label>
        </div>
        <div class="cs-panel">
            <div class="cs-panel-li">面板内容1</div>
            <div class="cs-panel-li">面板内容2</div>
            <div class="cs-panel-li">面板内容3</div>
        </div>
    </div>
</div>

image.png

<== 自定义单复选框 ==>

/* 单复选框的透明度为0并覆盖其它元素 */
[type="radio"],
[type="checkbox"] {
    position: absolute;
    width: 20px;
    height: 20px;
    opacity: 0;
    cursor: pointer;
}

/* 自定义单选框样式 */
.cs-radio {
    display: inline-block;
    width: 20px;
    height: 20px;
    border: 1px solid gray;
    border-radius: 50%;
    background-color: #fff;
    box-sizing: border-box;
    vertical-align: -0.5ex;
    user-select: none;
    transition: border-color 0.2s;
    overflow: hidden;
}

/* 选中 */
:focus + .cs-radio,
:checked + .cs-radio {
    border-color: deepskyblue;
}

/* 小圆点 */
:checked + .cs-radio::before {
    content: "";
    display: block;
    width: 10px;
    height: 10px;
    margin: 4px auto 0;
    border-radius: 50%;
    background-color: deepskyblue;
}

/* 禁用 */
:disabled + .cs-radio {
    background-color: #f5f5f5;
    opacity: 0.4;
}

/*复选框*/
.cs-checkbox {
    display: inline-block;
    width: 20px;
    height: 20px;
    border: 2px solid transparent;
    border-radius: 4px;
    color: gray;
    box-shadow: inset 0 0 0 1px;
    background-color: #fff;
    box-sizing: border-box;
    vertical-align: -0.5ex;
    user-select: none;
    transition: color 0.2s;
    overflow: hidden;
}

/* 选中 */
:focus + .cs-checkbox,
:checked + .cs-checkbox {
    color: deepskyblue;
}

/* ✓ */
:checked + .cs-checkbox::before {
    content: "";
    display: block;
    width: 8px;
    height: 3px;
    border-left: 2px solid;
    border-bottom: 2px solid;
    margin: 4px auto 0;
    -ms-transform: rotate(-45deg);
    transform: rotate(-45deg);
}

/* 禁用 */
:disabled + .cs-checkbox {
    background-color: #f5f5f5;
    opacity: 0.4;
}
<p>
    <!-- 原生的单选框 -->
    <input type="radio" id="radio1" name="radio" checked />
    <!-- 模拟的单选框 -->
    <label for="radio1" class="cs-radio"></label>
    <!-- 模拟的文本 -->
    <label for="radio1">单选项1</label>

    <input type="radio" id="radio2" name="radio" />
    <label for="radio2" class="cs-radio"></label>
    <label for="radio2">单选项2</label>

    <input type="radio" id="radio3" disabled />
    <label for="radio3" class="cs-radio"></label>
    <label for="radio3">单选项disabled</label>

    <input type="radio" id="radio4" checked disabled />
    <label for="radio4" class="cs-radio"></label>
    <label for="radio4">单选项checked + disabled</label>
</p>

<p>
    <!-- 原生的复选框 -->
    <input type="checkbox" id="checkbox" />
    <!-- 自己的复选框 -->
    <label for="checkbox" class="cs-checkbox"></label>
    <!-- 复选框的文本 -->
    <label for="checkbox">复选项</label>

    <input type="checkbox" id="checkbox2" disabled />
    <label for="checkbox2" class="cs-checkbox"></label>
    <label for="checkbox2">复选项disabled</label>

    <input type="checkbox" id="checkbox3" checked disabled />
    <label for="checkbox3" class="cs-checkbox"></label>
    <label for="checkbox3">复选项checked + disabled</label>
</p>

<== 开关效果 ==>

/* 隐藏原生的复选框 */
[type="checkbox"] {
    width: 44px;
    height: 26px;
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

/* 开关样式 */
.cs-switch {
    display: inline-block;
    width: 44px;
    height: 26px;
    border: 2px solid;
    border-radius: 26px;
    background-color: currentColor;
    box-sizing: border-box;
    color: silver;
    transition: all 0.2s;
    cursor: pointer;
}

/* 里面的球 */
.cs-switch::before {
    content: "";
    display: block;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background-color: #fff;
    transition: margin-left 0.2s;
}

/* 按下 */
:active:not(:disabled) + .cs-switch::before {
    box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.1);
}

/* 选中 */
:checked + .cs-switch {
    color: deepskyblue;
}

/* 选中的时候移动 */
:checked + .cs-switch::before {
    margin-left: 18px;
}

/* 聚焦 */
:focus-visible + .cs-switch {
    outline: 1px dotted Hightlight;
    outline: 5px auto -webkit-focus-ring-color;
}

/* 禁用 */
:disabled + .cs-switch {
    opacity: 0.4;
    cursor: default;
}
<!-- 普通状态 -->
<input type="checkbox" id="switch" />
<label class="cs-switch" for="switch"></label>

<!-- 选中状态 -->
<input type="checkbox" id="switch1" checked />
<label class="cs-switch" for="switch1"></label>

<!-- 禁用状态 -->
<input type="checkbox" id="switch2" disabled />
<label class="cs-switch" for="switch2"></label>

<!-- 选中禁用状态 -->
<input type="checkbox" id="switch3" checked disabled />
<label class="cs-switch" for="switch3"></label>

image.png

<== 复选框的标签选择 ==>

[type="checkbox"] {
    position: absolute;
    clip: rect(0 0 0 0);
}

.cs-topic {
    display: inline-block;
    width: 64px;
    margin-top: 5px;
    padding: 5px 0;
    border: 1px solid silver;
    text-align: center;
    cursor: pointer;
}

.cs-topic:hover {
    border-color: gray;
}

:checked + .cs-topic {
    border-color: deepskyblue;
    background-color: azure;
}

/* 选择计数器 */
body {
    counter-reset: topicCounter;
}

:checked + .cs-topic {
    counter-increment: topicCounter;
}

.cs-topic-counter::before {
    color: red;
    margin: 0 2px;
    content: counter(topicCounter);
}
<h4>标签的选择</h4>
请选择你感兴趣的话题:<br />
<input type="checkbox" id="topic1" /><label for="topic1" class="cs-topic">科技</label>
<input type="checkbox" id="topic2" /><label for="topic2" class="cs-topic">体育</label>
<input type="checkbox" id="topic3" /><label for="topic3" class="cs-topic">军事</label>
<input type="checkbox" id="topic4" /><label for="topic4" class="cs-topic">娱乐</label>
<input type="checkbox" id="topic5" /><label for="topic5" class="cs-topic">动漫</label>
<input type="checkbox" id="topic6" /><label for="topic6" class="cs-topic">音乐</label>
<input type="checkbox" id="topic7" /><label for="topic7" class="cs-topic">电影</label>
<input type="checkbox" id="topic8" /><label for="topic8" class="cs-topic">生活</label>
<p>您已选择<span class="cs-topic-counter"></span>个话题。</p>

image.png

<== 单选框的图像选择 ==>

[type="radio"] {
    position: absolute;
    clip: rect(0 0 0 0);
}

.cs-wallpaper {
    display: inline-block;
    width: 90px;
    height: 120px;
    margin-top: 5px;
    vertical-align: bottom;
    position: relative;
    overflow: hidden;
    cursor: pointer;
}

.cs-wallpaper-img {
    display: block;
    height: 100%;
    width: 100%;
}

:checked + .cs-wallpaper::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    border: 2px solid deepskyblue;
    background: linear-gradient(
        -135deg,
        deepskyblue 14px,
        transparent 15px
    );
}

:checked + .cs-wallpaper::after {
    content: "";
    position: absolute;
    right: 2px;
    top: 3px;
    width: 8px;
    height: 3px;
    color: white;
    border-left: 2px solid;
    border-bottom: 2px solid;
    -ms-transform: rotate(-45deg);
    transform: rotate(-45deg);
}
<h4>图像的选择</h4>
请选择壁纸:<br />
<input type="radio" id="wallpaper1" name="wallpaper" checked />
<label for="wallpaper1" class="cs-wallpaper">
    <img src="1.jpg" class="cs-wallpaper-img" />
</label>

<input type="radio" id="wallpaper2" name="wallpaper" />
<label for="wallpaper2" class="cs-wallpaper">
    <img src="2.jpg" class="cs-wallpaper-img" />
</label>

<input type="radio" id="wallpaper3" name="wallpaper" />
<label for="wallpaper3" class="cs-wallpaper">
    <img src="3.jpg" class="cs-wallpaper-img" />
</label>

<input type="radio" id="wallpaper4" name="wallpaper" />
<label for="wallpaper4" class="cs-wallpaper">
    <img src="4.jpg" class="cs-wallpaper-img" />
</label>

<input type="radio" id="wallpaper5" name="wallpaper" />
<label for="wallpaper5" class="cs-wallpaper">
    <img src="5.jpg" class="cs-wallpaper-img" />
</label>

<input type="radio" id="wallpaper6" name="wallpaper" />
<label for="wallpaper6" class="cs-wallpaper">
    <img src="6.jpg" class="cs-wallpaper-img" />
</label>

image.png

==> :indeterminate <==

说明: 这个伪类用来匹配半选状态,一般用在包含全选功能的列表中,没有原生的HTML属性支持半选状态,它只能通过JS来进行设置indeterminate属性来实现半选状态,它适用的元素有单选框复选框进度条

<== 与复选框结合 ==>

说明: 不同的浏览器效果是不一样的,这里展示Chrome浏览器的效果

<div class="cs-list">
    <table>
        <caption class="cs-list-caption">
            自定义复选框
        </caption>
        <thead>
            <tr>
                <th width="40">
                    <input type="checkbox" id="listAll" hidden />
                    <label for="listAll" class="cs-checkbox"></label>
                </th>
                <th>第1列</th>
                <th>第2列</th>
            </tr>
        </thead>
        
        <tbody>
            <tr>
                <td>
                    <input type="checkbox" id="list1" hidden />
                    <label for="list1" class="cs-checkbox"></label>
                </td>
                <td>数据1-1</td>
                <td>数据1-2</td>
            </tr>
            <tr>
                <td>
                    <input type="checkbox" id="list2" hidden />
                    <label for="list2" class="cs-checkbox"></label>
                </td>
                <td>数据2-1</td>
                <td>数据2-2</td>
            </tr>
            <tr>
                <td>
                    <input type="checkbox" id="list3" hidden />
                    <label for="list3" class="cs-checkbox"></label>
                </td>
                <td>数据3-1</td>
                <td>数据3-2</td>
            </tr>
        </tbody>
    </table>
</div>
.cs-list {
    display: inline-block;
    width: 260px;
    margin: 0 5px;
    vertical-align: top;
}

.cs-list-caption {
    margin-bottom: 10px;
}

/*复选框默认,全选和半选*/
.cs-checkbox {
    display: inline-block;
    width: 20px;
    height: 20px;
    border: 2px solid transparent;
    border-radius: 3px;
    color: gray;
    box-shadow: inset 0 0 0 1px;
    background-color: #fff;
    box-sizing: border-box;
    vertical-align: -0.5ex;
    user-select: none;
    transition: color 0.2s;
    overflow: hidden;
}

:checked + .cs-checkbox,
:indeterminate + .cs-checkbox {
    color: deepskyblue;
}

:checked + .cs-checkbox::before {
    content: "";
    display: block;
    width: 8px;
    height: 3px;
    border-left: 2px solid;
    border-bottom: 2px solid;
    margin: 4px auto 0;
    -ms-transform: rotate(-45deg);
    transform: rotate(-45deg);
}

:indeterminate + .cs-checkbox::before {
    content: "";
    display: block;
    width: 8px;
    border-bottom: 2px solid;
    margin: 7px auto 0;
}
// 全选半选的交互实现
document.addEventListener("click", function (event) {
    var target = event.target;
    if (target && target.type == "checkbox") {
        var table = target.closest("table");
        // 全选复选框
        var checkboxTh = table.querySelector("th input");
        var checkboxsTd = [].slice.call(table.querySelectorAll("td input"));
        // 点击全选按钮
        if (target == checkboxTh) {
            checkboxsTd.forEach(function (checkbox) {
                checkbox.checked = checkboxTh.checked;
            });
        } else {
            var isNoChecked = checkboxsTd.every(function (checkbox) {
                return checkbox.checked == false;
            }),

            isAllChecked = checkboxsTd.every(function (checkbox) {
                return checkbox.checked == true;
            });

            // 不选,全选和半选
            checkboxTh.indeterminate = false;

            if (isNoChecked) {
                checkboxTh.checked = false;
            } else if (isAllChecked) {
                checkboxTh.checked = true;
            } else {
                checkboxTh.indeterminate = true;
            }
        }
    }
});

image.png

<== 与单选框结合 ==>

特点: 当所有单选框元素的name属性是一样的时候,如果没有一个被选中则都会被应用伪类,有一个被选中就都不会被应用伪类;如果单选框元素没有name属性,那么当它自身没有被选中的时候它自己就会被应用伪类;因此可以用来提示用户尚未选择任何单选项

[type="radio"] {
    position: absolute;
    width: 20px;
    height: 20px;
    opacity: 0;
    cursor: pointer;
}

/* 单选框 */
.cs-radio {
    display: inline-block;
    width: 20px;
    height: 20px;
    border: 1px solid gray;
    border-radius: 50%;
    background-color: #fff;
    box-sizing: border-box;
    vertical-align: -0.5ex;
    user-select: none;
    transition: border-color 0.2s;
    overflow: hidden;
}

:checked + .cs-radio {
    border-color: deepskyblue;
}

:checked + .cs-radio::before {
    content: "";
    display: block;
    width: 10px;
    height: 10px;
    margin: 4px auto 0;
    border-radius: 50%;
    background-color: deepskyblue;
}

:indeterminate ~ .cs-valid-tips::before {
    content: "您尚未选择任何选项";
    color: red;
    font-size: 87.5%;
}
<input type="radio" id="radio1" name="radio" />
<label for="radio1" class="cs-radio"></label>
<label for="radio1">单选项1</label>

<input type="radio" id="radio2" name="radio" />
<label for="radio2" class="cs-radio"></label>
<label for="radio2">单选项2</label>

<input type="radio" id="radio3" name="radio" />
<label for="radio3" class="cs-radio"></label>
<label for="radio3">单选项3</label>

<!-- 这里显示提示信息 -->
<p class="cs-valid-tips"></p>

image.png

<== 与进度条结合 ==>

说明: 当进度条没有值的时候,就会匹配伪类

progress:indeterminate {
    background-color: deepskyblue;
    box-shadow: 0 0 0 2px black;
}
<progress min="1" max="100"></progress>
<progress min="1" max="100" value="50"></progress>

image.png

(3)输入值验证

说明: 这里的验证规则是交给浏览器的

==> :valid 和 :invalid <==

说明: 这两个可以理解为符合条件触发不符合条件触发,在使用的时候有一个缺点就是页面一加载就会进行验证,这样对用户的体验是不好的,毕竟还没使用就给提示,啥意思呢,此时可以使用:user-invalid来代替,它会在用户进行交互操作之后触发验证匹配

举例: 默认不开启验证,当用户产生提交表单的行为的时候,通过给表单添加特定的类名,触发浏览器的内置验证开始

<form id="csForm" novalidate>
    <p>
        验证码:<input
            class="cs-input"
            placeholder=" "
            required
            pattern="\w{4,6}"
        />
        <span class="cs-valid-tips"></span>
    </p>
    <input type="submit" value="提交" />
</form>
.cs-input {
    border: 1px solid gray;
    padding: 5px;
    width: 100px;
}

.valid .cs-input:invalid {
    border-color: red;
}

.valid .cs-input:valid + .cs-valid-tips::before {
    content: "√";
    color: green;
}

.valid .cs-input:invalid + .cs-valid-tips::before {
    content: "不符合要求";
    color: red;
}

.valid .cs-input:placeholder-shown + .cs-valid-tips::before {
    content: "尚未输入值";
}
csForm.addEventListener("submit", function (event) {
    this.classList.add("valid");
    event.preventDefault();
});

image.png

==> :in-range 和 :out-of-range <==

说明: 如果输入框的值在min和max的范围中,则触发in-range,否则就触发out-of-range

注意: 一般情况下,这两个伪类与元素的max和min属性息息相关,如果没有这两个属性,这两个伪类就会不匹配,但是Chrome浏览器下有一种特殊情况就是value类型与指定的type类型不匹配,也会匹配in-range的伪类;range类型的input元素是不会触发最后一个伪类的

<input type="number" min="1" max="100" />
<input type="range" min="1" max="100" />
input:in-range {
    outline: 2px dashed green;
}

input:out-of-range {
    outline: 2px dashed red;
}

image.png

==> :required 和 :optional <==

说明: 前者是用来匹配设置了required属性的表单元素,后者是表单元素如果没有设置required属性,就会匹配,这两个是对立的

注意: 对于单选框组的:required的匹配,即使它们的name属性值相同,也只会匹配存在required属性的那个元素;但是对于:invalid,如果单选框组的name属性都相同,那么会匹配所有的单选框元素

举例: 调查问卷的简单实现

.cs-ques-ul {
    counter-reset: quesIndex;
}

.cs-ques-li {
    display: table;
    width: 100%;
    position: relative;
}

/* 使用css计数器重现序号 */
.cs-ques-li::before {
    counter-increment: quesIndex;
    content: counter(quesIndex) ".";
    position: absolute;
    top: 0.75em;
    margin: 0 0 0 -20px;
}

/* 用于将标题显示在上方 */
.cs-caption {
    display: table-caption;
    caption-side: top;
}

/* 标记必选还是可选 */
:optional ~ .cs-caption::after {
    content: "(可选)";
    color: gray;
}

:required ~ .cs-caption::after {
    content: "(必选)";
    color: red;
}
<form>
    <fieldset>
        <legend>问卷调查</legend>
        <ol class="cs-ques-ul">
            <li class="cs-ques-li">
                <input type="radio" name="ques1" required />1-3年
                <input type="radio" name="ques1" />3-5年
                <input type="radio" name="ques1" />5年以上
                <!-- 这样放置方便使用兄弟选择符进行控制 -->
                <h4 class="cs-caption">你从事前端几年了?</h4>
            </li>
            
            <li class="cs-ques-li">
                <input type="radio" name="ques2" required />1-3小时
                <input type="radio" name="ques2" />3-5小时
                <input type="radio" name="ques2" />小时以上
                <h4 class="cs-caption">你每周多少业余时间用来学习前端技术?</h4>
            </li>
            
            <li class="cs-ques-li">
                <input type="radio" name="ques3" required />1-3本
                <input type="radio" name="ques3" />3-5本
                <input type="radio" name="ques3" />5本以上
                <h4 class="cs-caption">你每年买多少本前端相关数据?</h4>
            </li>
            
            <li class="cs-ques-li">
                <textarea></textarea>
                <h4 class="cs-caption">有什么其它想说的?</h4>
            </li>
        </ol>
        <p><input type="submit" value="提交" /></p>
    </fieldset>
</form>

image.png

十、树结构伪类

(1):root

说明: :root表示的就是html元素,假设给html元素加上一个类名,那么下面两种写法都是可以的,那么它和html标签选择器的区别在哪里呢,区别在于:root的优先级更高、IE9+的浏览器才支持以及:root指所有XML格式文档的跟元素

作用: 由于浏览器都支持css变量,那么有一些变量是全局的,根据规定,:root负责变量、标签选择器负责样式

<div class="html"></div>
:root.html { }
.html:root { }

(2):empty

作用范围: 它可以匹配空标签元素、双标签的替换元素和单标签元素

<!-- 空标签元素 -->
<div></div>

<!-- 双标签的替换元素 -->
<textarea></textarea>

<!-- 单标签元素 -->
<img src="" alt="" />

注意: 如果元素内存在注释、空格、换行的话,:empty伪类是无法匹配的

(3)子索引伪类

说明: 这里的伪类只能作用于标签元素上面,文本节点和注释节点是无法匹配的

==> :first-child 和 :last-child <==

说明: :first-child用于匹配第一个子元素,:last-child用于匹配最后一个子元素

ol > :first-child {
    color: red;
}

ol > :last-child {
    color: pink;
}
<ol>
    <li>内容</li>
    <li>内容</li>
    <li>内容</li>
</ol>

image.png

注意: 由于:first-child最先被浏览器支持,因此在两者都能使用的情况下,优先选择:first-child

==> :only-child <==

说明: 就是不能存在相同标签的兄弟元素,这个与相不相邻无关,只要存在就失效,一般在动态场景下面很常见,比如下面这个

.cs-loading {
    height: 150px;
    position: relative;
    text-align: center;
    border: 1px dotted;
}

/* 图片和文字同时存在时在中间留点间距 */
.cs-loading-img {
    width: 32px;
    height: 32px;
    margin-top: 45px;
    vertical-align: bottom;
}

.cs-loading-p {
    margin: 0.5em 0 0;
    color: gray;
}

/* 只有图片的时候居中绝对定位 */
.cs-loading-img:only-child {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    margin: auto;
}

/* 只有文字的时候行号近似垂直居中 */
.cs-loading-p:only-child {
    margin: 0;
    line-height: 150px;
}
<!-- 1. 只有加载图片 -->
<div class="cs-loading">
    <img src="./loading.png" class="cs-loading-img" />
</div>

<!-- 2. 只有加载文字 -->
<div class="cs-loading">
    <p class="cs-loading-p">正在加载中...</p>
</div>

<!-- 3. 加载图片和加载文字同时存在 -->
<div class="cs-loading">
<img src="./loading.png" class="cs-loading-img" />
    <p class="cs-loading-p">正在加载中...</p>
</div>

image.png

==> :nth-child() 和 :nth-last-child() <==

说明: 这两个区别在于前者是从前到后匹配,后者是从后往前匹配,都是匹配指定索引序号的元素,这里以前者为例说明

适用场景: 内容动态、无法确定的匹配

<== 语法 ==>

说明: 支持一个参数,这个参数是必传的,参数有两种形式,一种是关键字,一种是表达式

  • 关键字: 这里的关键字有两个,一个是odd,表示匹配第奇数个元素,而even表示匹配第偶数个元素

  • 表达式: 其格式为An+B,其中A和B的值是自己规定的整数,n表示一个倍数关系,其取值能从0开始,默认是1

/* 匹配表格的奇数行 */
tr:nth-child(odd) {}

/* 匹配表格的偶数行 */
tr:nth-child(even) {}

/* 匹配表格的第三行 */
tr:nth-child(3) {}

/* 匹配表格的第4、7、10...行 */
tr:nth-child(3n+4) {}

/* 匹配表格的前3行,因为元素的索引最小为1 */
tr:nth-child(-n+3) {}

/* 根据前面的规则,匹配表格的第4-10行 */
tr:nth-child(n+4):nth-child(-n+10) {}

<== 固定区间列表高亮效果 ==>

/* 前3行 */
tr:nth-child(-n + 3) td {
    background: cornsilk;
}

/* 4-10行 */
tr:nth-child(n + 4):nth-child(-n + 10) td {
    background: mintcream;
}
<table>
    <thead>
        <tr>
            <th scope="col" width="60">排名</th>
            <th scope="col">姓名</th>
            <th scope="col">总积分</th>
        </tr>
    </thead>
    <tr>
        <td>1</td>
        <td><a href>XboxYan</a></td>
        <td>105</td>
    </tr>
    <tr>
        <td>2</td>
        <td><a href>liyongleihf2006</a></td>
        <td>78</td>
    </tr>
    ...
</table>

image.png

(4)匹配类型的子索引伪类

特点: 下面的伪类在嵌套的标签中也能使用,这个需要自己理解下,想出来后就觉得有趣了

==> :first-of-type 和 :last-of-type <==

说明: 简单理解就是与当前这一层标签的第一个或者最后一个进行匹配,什么意思呢,看下面的例子你就明白了,这里可能说的有点模棱两可的,但是你自己试一下就觉得好像是这样的

特点: 以每一层为一个单位,嵌套的也作为一层

<div>
    <span>第一列</span>
    <div>diyihang</div>
    <div>diyihang</div>
    <div>diyihang</div>
</div>

<div>2</div>

<div>3</div>

<div>
    <span>最后一列</span>
    <div>555</div>
    <div>666</div>
    <div>777</div>
</div>
div:first-of-type {
    border: 2px dashed red;
}

div:last-of-type {
    border: 2px dashed black;
}

image.png

==> :only-of-child <==

说明: 在理解上面那一组的情况下,这个就是元素不能有相同的兄弟,与相不相5邻无关,只要存在就不能匹配,当然它也是嵌套的,看例子

特点: 以每一层作为一个单位,嵌套的也作为一层

<div>
    <span>第一列</span>
    <div>diyihang</div>
</div>
div:only-of-type {
    border: 2px dashed red;
}

image.png

==> :nth-of-type() 和 :nth-last-of-type() <==

说明: 这个也是按照序号去匹配元素的,基本使用跟前面的nth-child一样就不说了,区别主要在于嵌套,也看个例子理解下

特点: 每一个元素会作为一层进行判断,这里就包括嵌套了

<div>
    <span>第一列</span>
    <div>diyihang</div>
    <div>
        <span>ahahahhah</span>
        <div>AAAAA</div>
        <div>BBBBB</div>
    </div>
    <div>diyihang</div>
</div>

<div>2</div>

<div>3</div>

<div>
    <span>最后一列</span>
    <div>555</div>
    <div>666</div>
    <div>777</div>
</div>
/* 这里选择的是第一个,你会发现每一层的第一个都会匹配上 */
div:nth-of-type(1) {
    border: 2px dashed red;
}

image.png

十一、逻辑组合伪类

(1):not()

作用: 如果当前元素与括号里面的选择器不匹配,则该伪类会进行匹配

常见写法:

/* 匹配所有不是p元素的元素,当然html和body也不是p元素 */
:not(p) {}

/* 可以无限级联 */
input:not(:disabeld):not(:read-only) {}

注意: :not()伪类的优先级是0,它本身没有任何优先级,最终整个选择器的优先级是由括号里面选择器的优先级决定的

(2):is()

作用: 用于简化选择器,用起来有一种结合律的感觉

/* 选择器的作用相同 */
:is(a, b, c) > img {}

a > img,
b > img,
c > img {}

注意: 其优先级由括号里面优先级最高的那个选择器决定

/* 选择器的优先级相同 */
:is(.a, div) p {}

.a p {}

(3):where()

说明: 括号里面的选择器会被完全忽略掉,整个伪类的优先级永远是0,但是作用还是简化选择器

/* 选择器优先级相同 */
:where(.a, div) p {}

p {}