09-技巧篇:选择器
juejin_logo copyCreated with Sketch.

前言

很多同学的CSS编码习惯都是清一色的类而无相应选择器,层层嵌套的标签都包括至少一个类。有些同学会问,很多文章都说选择器有性能问题,为何还使用选择器?

是的,选择器与类对比起来在性能上确实无后者好,但如今浏览器对CSS的解析速度已得到大大提升,完全可忽略选择器那丁点的性能问题。本章不讲述选择器的性能问题,而是讲述如何用好选择器。

分类

先来对选择器做一个功能性的分类。当然熟悉全部选择器是玩转CSS的最最最最最基本功。因为选择器的标准概念中并无作出明确的分类,以下的分类是为了方便记忆而整理的。

基础选择器

选择器别名说明版本
tag标签选择器指定类型的标签1
#idID选择器指定身份的标签1
.class类选择器指定类名的标签1
*通配选择器所有类型的标签2

层次选择器

选择器别名说明版本
elemP elemC后代选择器元素的后代元素1
elemP>elemC子代选择器元素的子代元素2
elem1+elem2相邻同胞选择器元素相邻的同胞元素2
elem1~elem2通用同胞选择器元素后面的同胞元素3

集合选择器

选择器别名说明版本
elem1,elem2并集选择器多个指定的元素1
elem.class交集选择器指定类名的元素1

条件选择器

选择器说明版本
:lang指定标记语言的元素2
:dir()指定编写方向的元素4
:has包括指定元素的元素4
:is指定条件的元素4
:not非指定条件的元素4
:where指定条件的元素4
:scope指定元素作为参考点4
:any-link所有包括href链接元素4
:local-link所有包括href且属于绝对地址的链接元素4

状态选择器

选择器说明版本
:active鼠标激活的元素1
:hover鼠标悬浮的元素1
:link未访问的链接元素1
:visited已访问的链接元素1
:target当前锚点的元素3
:focus输入聚焦的表单元素2
:required输入必填的表单元素3
:valid输入合法的表单元素3
:invalid输入非法的表单元素3
:in-range输入范围内的表单元素3
:out-of-range输入范围外的表单元素3
:checked选项选中的表单元素3
:optional选项可选的表单元素3
:enabled事件启用的表单元素3
:disabled事件禁用的表单元素3
:read-only只读的表单元素3
:read-write可读可写的表单元素3
:target-within内部锚点元素处于激活状态的元素4
:focus-within内部表单元素处于聚焦状态的元素4
:focus-visible输入聚焦的表单元素4
:blank输入为空的表单元素4
:user-invalid输入合法的表单元素4
:indeterminate选项未定的表单元素4
:placeholder-shown占位显示的表单元素4
:current()浏览中的元素4
:past()已浏览的元素4
:future()未浏览的元素4
:playing开始播放的媒体元素4
:paused暂停播放的媒体元素4

结构选择器

选择器说明版本
:root文档的根元素3
:empty无子元素的元素3
:nth-child(n)元素中指定顺序索引的元素3
:nth-last-child(n)元素中指定逆序索引的元素3
:first-child元素中为首的元素2
:last-child元素中为尾的元素3
:only-child父元素仅有该元素的元素3
:nth-of-type(n)标签中指定顺序索引的标签3
:nth-last-of-type(n)标签中指定逆序索引的标签3
:first-of-type标签中为首的标签3
:last-of-type标签中为尾的标签3
:only-of-type父元素仅有该标签的标签3

属性选择器

选择器说明版本
[attr]指定属性的元素2
[attr=val]属性等于指定值的元素2
[attr*=val]属性包括指定值的元素3
[attr^=val]属性以指定值开头的元素3
[attr$=val]属性以指定值结尾的元素3
[attr~=val]属性包括指定值(完整单词)的元素(不推荐使用)2
[attr|=val]属性以指定值(完整单词)开头的元素(不推荐使用)2

伪元素

选择器说明版本
::before在元素前加入的内容2
::after在元素后加入的内容2
::first-letter元素的首字母1
::first-line元素的首行1
::selection鼠标选中的元素3
::backdrop全屏模式的元素4
::placeholder表单元素的占位4

优势

话说选择器若无用处,那W3C组织还干嘛把它纳入到标准中?选择器的劣势就不再讲述了,使用不当可能会引起解析性能问题,不过对于现代浏览器来说几乎可忽略,除非你还是IExplorer的忠实粉丝。使用选择器有什么好处?我给大家做个总结。

  • 减少很多无用或少用的类,保持css文件的整洁性与观赏性,代码也是一门艺术
  • 减少修改类而有可能导致样式失效的问题,有时修改类但未确保HTMLCSS一样而导致样式失效
  • 减少无实质性使用的类,例如很多层嵌套的标签,这些标签可能只用到一个属性,就无必要创建类关联
  • 对于那些结构与行为分离的写法,使用Scss/Less编写属性时结构会更清晰易读
  • 使用选择器可实现一些看似只能由JS才能实现的效果,既减少代码量也减少JSDOM的操作,使得交互效果更流畅

场景

因为选择器太多,我选择几个最具代表性的耍耍,通过选择器的妙用实现一些看似只能由JS才能实现的效果。未提到的选择器可能在其他地方穿插着讲述,请大家放心学习。

+与~

+/~都是作用于当前节点后的同胞节点,但两者有一个明显的区别,+是针对紧随该节点的节点,而~是针对后面所有节点,包括紧随该节点的节点。~还可针对一些指定类与选择器的节点,所以其使用性更广泛。

另外,+/~通常都会结合:checked完成一些高难度的CSS效果,当<input>触发了:checked选中状态后可通过+/~带动后面指定的节点声明一些特别属性。

通常其代码形式如下。

input:checked + div {}
input:checked ~ div {}

+/~的用途很广,静态效果与动态效果都能用上它,是两个很关键的选择器。以下通过动静结合的方式展示+/~的用途。

指定元素

<div class="specify-selector">
	<ul class="list">
		<li>同胞元素</li>
		<li class="next">当前元素</li>
		<li>同胞元素</li>
		<li>同胞元素</li>
		<li>同胞元素</li>
	</ul>
	<ul class="list">
		<li>同胞元素</li>
		<li class="next-all">当前元素</li>
		<li>同胞元素</li>
		<li>同胞元素</li>
		<li>同胞元素</li>
	</ul>
	<ul class="list">
		<li>同胞元素</li>
		<li class="next-filter">当前元素</li>
		<li>同胞元素</li>
		<li class="filter">同胞元素</li>
		<li>同胞元素</li>
	</ul>
</div>
<div class="specify-selector">
	<div class="button">
		<input id="btn1" type="radio" name="btns" hidden>
		<label for="btn1">点击我切换样式</label>
	</div>
	<div class="button">
		<input id="btn2" type="radio" name="btns" hidden>
		<label for="btn2">点击我切换样式</label>
	</div>
	<div class="button">
		<input id="btn3" type="radio" name="btns" hidden>
		<label for="btn3">点击我切换样式</label>
	</div>
</div>
.specify-selector {
	display: flex;
	& + .specify-selector {
		margin-top: 20px;
	}
	.list {
		border: 1px solid #f66;
		width: 200px;
		line-height: 2;
		font-weight: bold;
		font-size: 20px;
		color: #f66;
		& + .list {
			margin-left: 20px;
		}
		li {
			padding: 0 10px;
		}
		.next {
			background-color: #66f;
			color: #fff;
			& + li {
				background-color: #f90;
				color: #fff;
			}
		}
		.next-all {
			background-color: #66f;
			color: #fff;
			& ~ li {
				background-color: #09f;
				color: #fff;
			}
		}
		.next-filter {
			background-color: #66f;
			color: #fff;
			& ~ .filter {
				background-color: #09f;
				color: #fff;
			}
		}
	}
	.button {
		& + .button {
			margin-left: 20px;
		}
		label {
			display: block;
			padding: 0 10px;
			height: 40px;
			background-color: #3c9;
			cursor: pointer;
			line-height: 40px;
			font-size: 16px;
			color: #fff;
			transition: all 300ms;
		}
		input:checked + label {
			padding: 0 20px;
			border-radius: 20px;
			background-color: #f66;
		}
	}
}

:hover

:hover作用于鼠标悬浮的节点,是一个很好用的选择器。在指定场景可代替mouseentermouseleave两个鼠标事件,加上transtion让节点的动画更丝滑。

结合attr()有一个很好用的场景,就是鼠标悬浮在某个节点中显示提示浮层,提示浮层中包括着该动作的文本。

  • 给节点标记一个用户属性data-*
  • 当鼠标悬浮在该节点中触发:hover
  • 通过attr()获取data-*的内容
  • data-*的内容赋值到伪元素content

悬浮提示

<ul class="hover-tips">
	<li data-name="姨妈红"></li>
	<li data-name="基佬紫"></li>
	<li data-name="箩底橙"></li>
	<li data-name="姣婆蓝"></li>
	<li data-name="大粪青"></li>
	<li data-name="原谅绿"></li>
</ul>
$color-list: #f66 #66f #f90 #09f #9c3 #3c9;
.hover-tips {
	display: flex;
	justify-content: space-between;
	width: 200px;
	li {
		position: relative;
		padding: 2px;
		border: 2px solid transparent;
		border-radius: 100%;
		width: 24px;
		height: 24px;
		background-clip: content-box;
		cursor: pointer;
		transition: all 300ms;
		&::before,
		&::after {
			position: absolute;
			left: 50%;
			bottom: 100%;
			opacity: 0;
			transform: translate3d(0, -30px, 0);
			transition: all 300ms;
		}
		&::before {
			margin: 0 0 12px -35px;
			border-radius: 5px;
			width: 70px;
			height: 30px;
			background-color: rgba(#000, .5);
			line-height: 30px;
			text-align: center;
			color: #fff;
			content: attr(data-name);
		}
		&::after {
			margin-left: -6px;
			border: 6px solid transparent;
			border-top-color: rgba(#000, .5);
			width: 0;
			height: 0;
			content: "";
		}
		@each $color in $color-list {
			$index: index($color-list, $color);
			&:nth-child(#{$index}) {
				background-color: $color;
				&:hover {
					border-color: $color;
				}
			}
		}
		&:hover {
			&::before,
			&::after {
				opacity: 1;
				transform: translate3d(0, 0, 0);
			}
		}
	}
}

:valid与:invalid

很多同学可能还会使用JS去判断表单输入内容是否合法,其实HTML5发布后,可用CSS完成这些工作,正确搭配一些属性能大大减少校验表单的代码量。

完成一个完整的表单验证,需以下HTML属性与选择器搭配。

  • placeholder:占位,在未输入内容时显示提示文本
  • pattern:正则,在输入内容时触发正则验证
  • :valid:作用于输入合法的表单节点
  • :invalid:作用于输入非法的表单节点
<input type="text" placeholder="" pattern="">
input:valid {}
input:invalid {}

patternJS正则有点不同,JS的正则形式是/regexp/,而pattern的正则形式只需/regexp/中的regexp。校验过程是动态触发,监听input键盘事件,当输入内容合法时触发:valid,当输入内容非法时触发:invalid

表单内容

<form class="form-validation">
	<div>
		<label>名字</label>
		<input type="text" placeholder="请输入你的名字(1到10个中文)" pattern="^[\u4e00-\u9fa5]{1,10}$" required>
	</div>
	<div>
		<label>手机</label>
		<input type="text" placeholder="请输入你的手机" pattern="^1[3456789]\d{9}$" required>
	</div>
	<div>
		<label>简介</label>
		<textarea required></textarea>
	</div>
</form>
.form-validation {
	width: 500px;
	div + div {
		margin-top: 10px;
	}
	label {
		display: block;
		padding-bottom: 5px;
		font-weight: bold;
		font-size: 16px;
	}
	input,
	textarea {
		display: block;
		padding: 0 20px;
		border: 1px solid #ccc;
		width: 100%;
		height: 40px;
		outline: none;
		caret-color: #09f;
		transition: all 300ms;
		&:valid {
			border-color: #3c9;
		}
		&:invalid {
			border-color: #f66;
		}
	}
	textarea {
		height: 122px;
		resize: none;
		line-height: 30px;
		font-size: 16px;
	}
}

:checked

:checked作用于选项选中的表单节点,当<input>type设置为radiocheckbox时可用。在CSS神操作骚技巧中是一个很重要的技巧,主要是用于模拟鼠标点击事件。

切换按钮

<input class="switch-btn" type="checkbox">
.btn {
	border-radius: 31px;
	width: 102px;
	height: 62px;
	background-color: #e9e9eb;
}
.switch-btn {
	position: relative;
	appearance: none;
	cursor: pointer;
	transition: all 100ms;
	@extend .btn;
	&::before {
		position: absolute;
		content: "";
		transition: all 300ms cubic-bezier(.45, 1, .4, 1);
		@extend .btn;
	}
	&::after {
		position: absolute;
		left: 4px;
		top: 4px;
		border-radius: 27px;
		width: 54px;
		height: 54px;
		background-color: #fff;
		box-shadow: 1px 1px 5px rgba(#000, .3);
		content: "";
		transition: all 300ms cubic-bezier(.4, .4, .25, 1.35);
	}
	&:checked {
		background-color: #5eb662;
		&::before {
			transform: scale(0);
		}
		&::after {
			transform: translateX(40px);
		}
	}
}

<input><label>的巧妙搭配

上述提到与+/~的搭配使用,其实还有一个很重要的技巧,就是结合<label>使用。为何要结合<label>?因为要让input:checked + div {}input:checked ~ div {}起效,其HTML结构必须像以下那样。

<input type="radio">
<div></div>

这样就无法分离结构与行为了,导致CSS必须跟着HTML走,只能使用绝对定位将<input>固定到指定位置。使用<label>绑定<input>可将<input>的鼠标选择事件转移到<label>中,由<label>控制选中状态。那HTML结构可改成以下形式,此时的<input>可设置hidden隐藏起来,不参与任何排版。

<input type="radio" id="btn" hidden>
<div>
	<label for="btn">
</div>

<input>使用id<label>使用for关联起来,而hidden使<input>隐藏起来,不占用网页任何位置,此时<label>放置在网页任何位置都行。

input:checked + div {}
input:checked ~ div {}

使用CSS实现的标签导航是一个很好的学习用例。

标签导航

:focus-within

:focus-within作用于内部表单节点处于聚焦状态的节点。它监听当前节点中是否存在表单节点且该表单节点是否处于聚焦状态。

有些同学听上去可能觉得拗口,其实它是一个简单易用的选择器。表单控件触发focusblur两个鼠标事件后往祖先节点冒泡,在祖先节点中通过:focus-within捕获该冒泡事件声明样式。

冒泡响应

<form class="bubble-distribution">
	<h3>注册</h3>
	<div class="accout">
		<input type="text" placeholder="请输入手机或邮箱" pattern="^1[3456789]\d{9}$|^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$" required>
		<img src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/500c1180a96859e5c54a5359f024a397.svg">
	</div>
	<div class="password">
		<input type="password" placeholder="请输入密码(6到20位字符)" pattern="^[\dA-Za-z_]{6,20}$" required>
		<img src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/4f6f6f316cde4398d201cd67e44ddea3.svg">
	</div>
	<div class="code">
		<input type="text" placeholder="请输入邀请码(6位数字)" pattern="^[\d]{6}$" maxLength="6" required>
		<button type="button">查询</button>
		<img src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/500c1180a96859e5c54a5359f024a397.svg">
	</div>
	<img src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/ad7fa76844a2df5c03151ead0ce65ea6.svg">
	<ul>
		<li>
			<input id="male" type="radio" name="sex">
			<label for="male">Boy</label>
		</li>
		<li>
			<input id="female" type="radio" name="sex">
			<label for="female">Girl</label>
		</li>
	</ul>
	<button type="button">注册</button>
</form>
.bubble-distribution {
	position: relative;
	margin-top: 50px;
	padding: 25px;
	border-radius: 2px;
	width: 320px;
	background-color: #f0f0f0;
	h3 {
		font-size: 16px;
		color: #333;
	}
	div {
		margin-top: 10px;
	}
	img {
		position: absolute;
		left: 50%;
		bottom: 100%;
		margin: 0 0 -8px -60px;
		width: 120px;
	}
	ul {
		display: flex;
		justify-content: space-between;
		align-items: center;
		margin-top: 10px;
		height: 30px;
		line-height: 30px;
	}
	li {
		position: relative;
		width: 45%;
		transition: all 300ms;
		&:focus-within {
			background: linear-gradient(90deg, #09f 50%, transparent 0) repeat-x,
				linear-gradient(90deg, #09f 50%, transparent 0) repeat-x,
				linear-gradient(0deg, #09f 50%, transparent 0) repeat-y,
				linear-gradient(0deg, #09f 50%, transparent 0) repeat-y;
			background-position: 0 0, 0 100%, 0 0, 100% 0;
			background-size: 8px 1px, 8px 1px, 1px 8px, 1px 8px;
			animation: move 500ms infinite linear;
		}
	}
	input[type="text"],
	input[type="password"] {
		padding: 10px;
		border: 1px solid #e9e9e9;
		border-radius: 2px;
		width: 100%;
		height: 40px;
		outline: none;
		transition: all 300ms;
		&:focus:valid {
			border-color: #09f;
		}
		&:focus:invalid {
			border-color: #f66;
		}
	}
	input[type="radio"] {
		position: absolute;
		width: 0;
		height: 0;
		&:checked + label {
			border: 3px solid transparent;
			background-color: #09f;
			color: #fff;
		}
	}
	label {
		display: block;
		border-bottom: 1px solid #ccc;
		width: 100%;
		background-clip: padding-box;
		cursor: pointer;
		text-align: center;
		transition: all 300ms;
	}
	button {
		margin-top: 10px;
		border: none;
		border-radius: 2px;
		width: 100%;
		height: 40px;
		outline: none;
		background-color: #09f;
		cursor: pointer;
		color: #fff;
		transition: all 300ms;
	}
	.accout,
	.password,
	.code {
		img {
			display: none;
		}
		&:focus-within {
			img {
				display: block;
			}
			& ~ img {
				display: none;
			}
		}
	}
	.code {
		display: flex;
		justify-content: space-between;
		button {
			margin-top: 0;
		}
		input {
			&:not(:placeholder-shown) {
				width: 70%;
				& + button {
					width: 25%;
				}
			}
			&:placeholder-shown {
				width: 100%;
				& + button {
					width: 0;
					opacity: 0;
				}
			}
		}
	}
}
@keyframes move {
	to {
		background-position: 6% 0, -6% 100%, 0 -6%, 100% 6%;
	}
}

:empty

还有使用JS判断列表集合为空时显示占位符吗?相信很多使用MVVM框架开发的同学都会使用条件判断的方式渲染虚拟DOM,若列表长度不为0则渲染列表,否则渲染占位符。然而CSS提供了一个空判断的选择器:empty,可能很少同学会注意到。

:empty作用于无子节点的节点,该子节点也包括行内匿名盒(单独的文本内容)。以下三种情况都为非空状态,若不出现这三种状态则为空状态,此时:empty才会触发。

  • 仅存在节点:<div><p>CSS</p></div>
  • 仅存在文本:<div>CSS</div>
  • 同时存在节点与文本:<div>Hello <p>CSS</p></div>

清空状态

<ul class="empty-list">
	<li>Data 1</li>
	<li>Data 2</li>
	<li>Data 3</li>
	<li>Data 4</li>
	<li>Data 5</li>
	<li>Data 6</li>
	<li>Data 7</li>
	<li>Data 8</li>
	<li>Data 9</li>
	<li>Data 10</li>
</ul>
<ul class="empty-list"></ul>
$empty: "https://jowayyoung.github.io/static/img/icss/empty.svg";
.empty-list {
	overflow: auto;
	width: 200px;
	height: 150px;
	outline: 1px solid #3c9;
	&:empty {
		display: flex;
		justify-content: center;
		align-items: center;
		background: url($empty) no-repeat center/100px auto;
		&::after {
			margin-top: 90px;
			font-weight: bold;
			content: "没钱就没数据";
		}
	}
	& + .empty-list {
		margin-left: 20px;
	}
	li {
		padding: 0 10px;
		height: 30px;
		background-color: #09f;
		line-height: 30px;
		color: #fff;
		&:nth-child(even) {
			background-color: #f90;
		}
	}
}

::before与::after

有时为了实现某个效果而往网页中反复添加标签变得很繁琐,添加太多标签反而不好处理而变得难以维护。此时会引入伪元素的概念解决上述问题。

伪元素指网页中不存在的元素。伪元素HTML代码中未声明却能正常显示,在网页渲染时看到这些本来不存在的元素发挥着重要作用。:before:after是两个很重要的伪元素,早在CSS2就出现了。

起初伪元素的前缀使用单冒号语法。随着CSS改革,伪元素的前缀被修改成双冒号语法:before/:after从此变成::before/::after,用于区分伪类,未提到的伪元素同理。若兼容低版本浏览器,还需使用:before:after,但本课程都以::before/::after编写代码。

伪类伪元素虽然都是选择器,但它们还是存在一丝丝的差别。

  • 伪类通常是一些状态选择器,选择处于指定状态的DOM,例如:hover:focus:checked
  • 伪元素通常是一些实体选择器,选择满足指定条件的DOM,例如::selection::first-letter::first-line

两者最主要的区别就是伪类使用单冒号语法伪元素使用双冒号语法::before/::after必须结合content使用,通常用作修饰节点,为节点加入一些多余的东西,但又不想内嵌一些其他标签。若加入两个以下(包括两个)的修饰,建议使用::before/::after

以下两个HTML结构是等效的。

<p class="desc-1">
	<span>:before</span>CSS<span>:after</span>
</p>
<p class="desc-2">CSS</p>
.desc-2 {
	&::before {
		content: ":before";
	}
	&::after {
		content: ":after";
	}
}

::before/::after最常见场景就是气泡对话框,圆滚滚的身子带上一个三角形的尾巴。像以下第二个挖空的气泡对话框,其实使用白色填充背景颜色,而小尾巴使用白色的::after叠加橙色的::before形成障眼法。

气泡对话框

<div class="bubble-box">iCSS</div>
<div class="bubble-empty-box">iCSS</div>
.bubble-box {
	position: relative;
	border-radius: 5px;
	width: 200px;
	height: 50px;
	background-color: #f90;
	line-height: 50px;
	text-align: center;
	font-size: 20px;
	color: #fff;
	&::after {
		position: absolute;
		left: 100%;
		top: 50%;
		margin-top: -5px;
		border: 5px solid transparent;
		border-left-color: #f90;
		content: "";
	}
}
.bubble-empty-box {
	position: relative;
	margin-top: 10px;
	border: 2px solid #f90;
	border-radius: 5px;
	width: 200px;
	height: 50px;
	line-height: 46px;
	text-align: center;
	font-size: 20px;
	color: #f90;
	&::before {
		position: absolute;
		left: 100%;
		top: 50%;
		margin: -5px 0 0 2px;
		border: 5px solid transparent;
		border-left-color: #f90;
		content: "";
	}
	&::after {
		position: absolute;
		left: 100%;
		top: 50%;
		margin-top: -4px;
		border: 4px solid transparent;
		border-left-color: #fff;
		content: "";
	}
}

留言
Ctrl + Enter
全部评论(16)
迟来的代码搬运工的头像
删除
前端码农
被问到伪类有什么优点,这要怎么答呀[流泪]
点赞
1
删除
(作者)
简化HTML结构,更加语义化,节省渲染节点的开销
1
回复
bigFace的头像
删除
变量和选择器章节标题反了吧
点赞
回复
巴山却话的头像
删除
培训讲师 @ 北京振涛弘业教育咨询有限公司
发现 CSS 慢慢能够完成一些原来 JS 才能完成的效果,在实际开发中,某个效果,应该优先选择哪种实现方式呢?欢迎大家留言
点赞
回复
e195的头像
删除
作者你好 我发现:hover的那个demo好像有点问题 鼠标悬浮在::before元素上也会使弹窗跳出 本来的话鼠标应该只悬浮在li标签上才会跳出 这里的::before元素不知道为什么也被加上了hover 图片中打圆圈的地方是我鼠标悬停的位置
点赞
3
删除
opacity隐藏元素可以触发事件,给伪元素加上pointer-events:none可以解决
2
回复
删除
回复
谢谢
opacity隐藏元素可以触发事件,给伪元素加上pointer-events:none可以解决
点赞
回复
查看更多回复
sunmaer的头像
删除
前端开发工程师 @ ByteDance
empty选择器学到了👍
点赞
回复
Macks的头像
删除
前端斟茶工程师
您好,请问:checked (switch)按钮那里用::before的用意是什么?
点赞
1
删除
(作者)
用来模拟按钮背景的缩放渐变,相当是一层在按钮背景上的布,点击按钮时这块布快速缩小,那样就变换颜色了,其实就是一种障眼法
1
回复
alfred_li的头像
删除
前端 @ ringcentral
全是干货👍
3
1
删除
(作者)
谢谢支持
点赞
回复