14-实战篇:实战大操作-表单控件
juejin_logo copyCreated with Sketch.

前言

表单是网页中最实用的视觉元素,而表单校验又是表单中最实用的操作。普通情况下,会结合Angular/React/VueMVVM框架完成相关表单校验功能。

本次通过CSS完成一个可切换的登录注册模块,通过选择器实现一些看似只能由JS才能实现的效果,具体用到以下选择器。

  • +:相邻同胞选择器
  • ~:通用同胞选择器
  • :not():非指定条件的元素
  • :hover:鼠标悬浮的元素
  • :focus:输入聚焦的表单元素
  • :valid:输入合法的表单元素
  • :invalid:输入非法的表单元素
  • :checked:选项选中的表单元素
  • :placeholder-shown:占位显示的表单元素
  • :nth-child(n):元素中指定顺序索引的元素

本章的主题是表单控件,主要是通过上述选择器简化基于JS逻辑的效果。常见控件有登录注册

登录注册

登录注册模块是每个网站都可能具备的模块。本次实战需求是通过CSS编写一个登录注册模块,当然排除登录与注册的按钮点击事件。

首先需明确有哪些功能。

  • 切换登录注册两个模块,可用~:checkednth-child(n)实现
  • 悬浮模块导航时显示选中状态,可用:hover实现
  • 判断输入框是否进入输入状态并校验内容,可用:focus:valid:invalid实现
  • 判断输入框是否存在内容,可用+:not()placeholder-shown实现

总体来说可将登录注册模块分为两部分,分别是模块切换与表单校验。

模块切换

还记得第9章如何构造这种切换效果吗?实现原理主要是结合<input><label><input>使用id<label>使用for关联起来,而hidden使<input>隐藏起来,不占用网页任何位置,此时<label>放置在网页任何位置都行。

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

还记得第9章的切换按钮的刹车动画吗?也搬过来使用吧,对使用刹车动画的节点声明transition:all 300ms cubic-bezier(.4,.4,.25,1.35)即可。

登录注册-1

<div class="auth">
	<input id="login-btn" type="radio" name="auth" checked hidden>
	<input id="logon-btn" type="radio" name="auth" hidden>
	<div class="auth-title">
		<label for="login-btn">登录</label>
		<label for="logon-btn">注册</label>
		<em></em>
	</div>
	<div class="auth-form">
		<form>登录</form>
		<form>注册</form>
	</div>
</div>
.auth {
	overflow: hidden;
	border-radius: 2px;
	width: 320px;
	background-color: #f0f0f0;
	.auth-title {
		display: flex;
		position: relative;
		border-bottom: 1px solid #eee;
		height: 40px;
		label {
			display: flex;
			justify-content: center;
			align-items: center;
			flex: 1;
			height: 100%;
			cursor: pointer;
			transition: all 300ms;
			&:hover {
				color: #66f;
			}
		}
		em {
			position: absolute;
			left: 0;
			bottom: 0;
			border-radius: 1px;
			width: 50%;
			height: 2px;
			background-color: #f66;
			transition: all 300ms cubic-bezier(.4, .4, .25, 1.35);
		}
	}
	.auth-form {
		display: flex;
		width: 200%;
		height: 250px;
		transition: all 300ms cubic-bezier(.4, .4, .25, 1.35);
		form {
			flex: 1;
			padding: 20px;
		}
	}
}
#login-btn:checked {
	& ~ .auth-title {
		label:nth-child(1) {
			font-weight: bold;
			color: #f66;
		}
		em {
			transform: translate(0, 0);
		}
	}
	& ~ .auth-form {
		transform: translate(0, 0);
	}
}
#logon-btn:checked {
	& ~ .auth-title {
		label:nth-child(2) {
			font-weight: bold;
			color: #f66;
		}
		em {
			transform: translate(160px, 0);
		}
	}
	& ~ .auth-form {
		transform: translate(-50%, 0);
	}
}

表单校验

还记得第9章的表单校验吗?通过以下方面结合能完成一个完整的表单校验。

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

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

以手机输入框为例,需满足以下结构与样式。

<input type="text" placeholder="请输入手机" pattern="^1[3456789]\d{9}$" required>
input:valid {}
input:invalid {}

存在一个问题,若直接声明input:validinput:invalid,在网页初始化后或输入框内容为空时都会触发:invalid,导致表单校验还未开始就显示校验不通过的样式。为了只在输入内容时才触发:valid:invalid,可在其前面添加:focus,表示在表单处于聚焦状态时才触发某些行为。

input:focus:valid {}
input:focus:invalid {}

在输入内容时,有内容与无内容可通过:placeholder-shown判断。:placeholder-shown表示占位显示的表单元素,而占位不显示的表单元素可用:not()取反,再结合+带动紧随该节点的节点。

  • 有内容就无占位::not(:placeholder-shown)
  • 无内容就有占位::placeholder-shown

本次实战主要是为了将上述选择器结合起来使用而提供一种场景,所以不写太多复杂的输入框了。将登录模块的HTML结构复制一份到注册模块,哈哈!最终代码如下。

登录注册

<div class="auth">
	<input id="login-btn" type="radio" name="auth" checked hidden>
	<input id="logon-btn" type="radio" name="auth" hidden>
	<div class="auth-title">
		<label for="login-btn">登录</label>
		<label for="logon-btn">注册</label>
		<em></em>
	</div>
	<div class="auth-form">
		<form>
			<div>
				<input type="text" placeholder="请输入手机" pattern="^1[3456789]\d{9}$" required>
				<label>手机</label>
			</div>
			<div>
				<input type="password" placeholder="请输入密码(6到20位字符)" pattern="^[\dA-Za-z_]{6,20}$" required>
				<label>密码</label>
			</div>
			<button type="button">登录</button>
		</form>
		<form>
			<div>
				<input type="text" placeholder="请输入手机" pattern="^1[3456789]\d{9}$" required>
				<label>手机</label>
			</div>
			<div>
				<input type="password" placeholder="请输入密码(6到20位字符)" pattern="^[\dA-Za-z_]{6,20}$" required>
				<label>密码</label>
			</div>
			<button type="button">登录</button>
		</form>
	</div>
</div>
.auth {
	overflow: hidden;
	border-radius: 2px;
	width: 320px;
	background-color: #f0f0f0;
	.auth-title {
		display: flex;
		position: relative;
		border-bottom: 1px solid #eee;
		height: 40px;
		label {
			display: flex;
			justify-content: center;
			align-items: center;
			flex: 1;
			height: 100%;
			cursor: pointer;
			transition: all 300ms;
			&:hover {
				color: #66f;
			}
		}
		em {
			position: absolute;
			left: 0;
			bottom: 0;
			border-radius: 1px;
			width: 50%;
			height: 2px;
			background-color: #f66;
			transition: all 300ms cubic-bezier(.4, .4, .25, 1.35);
		}
	}
	.auth-form {
		display: flex;
		width: 200%;
		height: 250px;
		transition: all 300ms cubic-bezier(.4, .4, .25, 1.35);
		form {
			flex: 1;
			padding: 20px;
		}
		div {
			display: flex;
			flex-direction: column-reverse;
			& + div {
				margin-top: 10px;
			}
		}
		input {
			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;
			}
			&:not(:placeholder-shown) + label {
				height: 30px;
				opacity: 1;
				font-size: 14px;
			}
		}
		label {
			overflow: hidden;
			padding: 0 10px;
			height: 0;
			opacity: 0;
			line-height: 30px;
			font-weight: bold;
			font-size: 0;
			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;
		}
	}
}
#login-btn:checked {
	& ~ .auth-title {
		label:nth-child(1) {
			font-weight: bold;
			color: #f66;
		}
		em {
			transform: translate(0, 0);
		}
	}
	& ~ .auth-form {
		transform: translate(0, 0);
	}
}
#logon-btn:checked {
	& ~ .auth-title {
		label:nth-child(2) {
			font-weight: bold;
			color: #f66;
		}
		em {
			transform: translate(160px, 0);
		}
	}
	& ~ .auth-form {
		transform: translate(-50%, 0);
	}
}

留言
Ctrl + Enter
全部评论(7)
zb本尊的头像
删除
我感觉表单验证有个缺点,就是一旦开始输入就会立刻验证正确与否,这样当没有输入完整的时候,就会一直提醒错误
1
3
删除
(作者)
目前很多网站的都是这样处理的,因为无法知道用户到底有没有输入完成
2
回复
删除
验证验证的时机可以是输入中可以是blur,可以把focus换blur试试
点赞
回复
查看更多回复
console_man的头像
删除
Web前端
👍👍👍非常使用的技巧
点赞
回复
唐诗的头像
删除
@pc
倒数第二章图 它裂了
点赞
1
删除
(作者)
重新刷新下就好
点赞
回复