【jquery.validation】后端程序员:这是我写过的、堪称完美的登录表单

效果图

  

前言

作为一名学习服务端开发方向的学生来说,虽然自己有学过前端,但并不算深入,而且个人觉得写前端比较繁琐,再加上自己的审美一般,所以真写不出能让自己满意的界面。对于大多数后端程序员来说,应该宁愿拿现成的静态资源模板来改,也不愿意自己从无到有去写吧?即使,有了解过一些前端框架,写出让人觉得舒服的界面还是比较费劲。

上面登录表单,看起来还算比较舒服,是我JavaWeb课程的一个小作业,我并没有用老师提供的静态页面,因为太难看了。。哈哈哈。

下面我站在前端角度,分析一下如何实现这样的一个登录表单。当然我也会简要说明一下所需要的后端接口。

详情参看代码,代码中写了简要注释,标出了要注意的细节。

分析

1、输入框(包括绿色的ok提示,红色的error提示)、按钮等等,这些组件,我们当然不会自己手写啦,手写也很难做出这么好看。我选用了 bootstrap 这个比较容易入手的前端框架,说实话,我也没有专门学过,直接去官网看文档就行了,然后复制粘贴,再自己微调一下样式。自己在写的时候,也要注意用上 bootstrap响应式特点,让界面能够适应手机设备。

2、从上面的 gif 可以看出来,这个登录表单的验证做的非常 严格和细致。我们来分析一下这个表单验证的逻辑,究竟 细致 到什么程度?

验证规则

(1)邮箱不能为空、邮箱必须符合正则表达式

(2)异步请求后端来验证 邮箱,如果邮箱存在,后端会返回账户的头像URL,前端会更新顶部的圆形头像

(3)密码不能为空

(4)异步验证验证码

验证触发

当某个输入框失去焦点时,验证该表单项是否符合 相应的规则(可能不止一条,比如说邮箱)。只要有任意一条规则不通过,点击“登录”按钮就无法提交表单。

信息提示

信息提示包括两种:绿色的OK提示,红色的error提示。两种提示 各自 包括了3个细节:文字提示、图标提示、输入框的边框变色。当然,这些样式都来自于 bootstrap,但是我们需要根据验证触发和验证来控制样式的切换。

即使用 jquery 来写,这样复杂繁琐的逻辑验证,写起来也会非常棘手,因为细节太多了!!!这个时候我们就要用到了一款基于jquery的插件了: jquery.validation.min.js 。这个插件可以直接去官网下载,我这里在网上找到一个别人的修改版,在原有 jquery.validation 增加了一些常用的验证规则。当然,干这个的,肯定是比较强的前端工程师。学后端的我,目前没有这个能力。

后端接口

1、验证邮箱是否存在,存在返回用户头像:/account/photo

2、生成验证码:/account/captcha/generate

3、验证验证码是否正确:/account/captcha/validate

4、登录:/account/login

接口都比较简单,传参我也省略了。学过的SpringBoot应该很容易写出来。学习需要,我是用Maven徒手整合SSM来写的。用起来真没有SpringBoot舒服。

资料

boostrap官网:https://v3.bootcss.com/css/

jquery.validation 增强版:https://download.csdn.net/download/qq_43290318/13109073

jquery.validation 的使用详解:https://blog.csdn.net/wangxiaoan1234/article/details/77466720

代码

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	String contextPath = request.getContextPath();
%>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<!-- 不设置的话,手机端不会进行响应式布局 -->
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>登录</title>

		<!-- 引入Bootstrap核心样式文件(必须) -->
		<link rel="stylesheet" href="lib/bootstrap/css/bootstrap.min.css">

		<!-- 你自己的样式或其他文件 -->
		<link rel="stylesheet" href="css/login.css">

		<!--站点图标-->
		<!-- ... -->
		
	</head>
	<body>
		<div class="container">
			<div class="row">
				<div class="col-sm-4 col-sm-offset-4 panel panel-default login-box">
					<div class="panel-body">
						<img class="img-circle photo" src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605206616385&di=5a3e31c19b07f422adf49c37505f5126&imgtype=0&src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fitem%2F202007%2F07%2F20200707113705_VFJvw.thumb.400_0.jpeg"
						 alt="头像">
						<form id="loginForm" onSubmit="return false;">
							<!-- 
							关于bootstrap输入框提示
							父容器div的has-feedback不能少,has-success和has-error选其一
							-->
							<div class="form-group has-feedback">
								<label for="email">邮箱</label>
								<!-- 错误提示信息 -->
								<span class="error-msg"></span>
								<input type="email" class="form-control" id="email" name="email" placeholder="邮箱">
								<!-- 图标,打勾或者打叉。glyphicon-ok和glyphicon-remove -->
								<span class="glyphicon form-control-feedback"></span>				
							</div>
							<div class="form-group clear-float has-feedback">
								<label for="password">密码</label>
								<span class="error-msg"></span>
								<input type="password" class="form-control" id="password" name="password" placeholder="密码">
								<span class="glyphicon form-control-feedback"></span>	
							</div>
							<div class="form-group has-feedback">
								<label for="captcha">验证码</label>
								<span class="glyphicon glyphicon-refresh refresh" onClick="refreshCaptcha()"></span>
								<span class="error-msg"></span>
								<input type="text" maxlength="4" class="form-control captcha" id="captcha" name="captcha" 
								placeholder="验证码" >
								<span class="glyphicon form-control-feedback captcha_icon"></span>	
							</div>
							<!-- 注意表单中必须有type="submit"的按钮,否则表单验证通过后,无法进入回调函数submitHandler() -->
							<button type="submit" class="btn btn-success login-btn">登&nbsp;&nbsp;录</button>
							<button type="button" class="btn btn-link to-register">还没有账号?去注册 >></button>
						</form>
					</div>
				</div>
			</div>
		</div>
		
		
		<script src="lib/jquery/jquery.min.js"></script>
		<script src="lib/jquery/jquery.validation.min.js"></script>
		<!-- 引入所有的Bootstrap的JS插件 -->
		<script src="lib/bootstrap/js/bootstrap.min.js"></script>
		
		<script src="js/login.js"></script>
	</body>
</html>

login.css

下面的css都比较基础,主要我学习web前端选修课时,学的也不算很深入,都是一些基础的东西。。虽是如此,但是因为不熟,调样式的时候踩了不少坑。

.clear-float {
	clear: both;
}
.login-box {
	/* border: 1px solid #000; */
	padding: 0 16px;
	border-radius: 8px;
	margin-top: 100px;
	box-shadow: 0 0 10px #ddd;
}
.photo {
	/* 一定要设置为块状元素,否则margin auto不生效 */
	display: block;
	width: 100px;
	height: 100px;
	border: 1px solid #eee;
	padding: 4px;
	box-shadow: 0 0 10px #ddd;
	background-color: #fff;
	margin: 0 auto;
}
.login-btn {
	width: 100%;
	/* 设置line-height比设置height更好 */
	line-height: 24px; 
	/* height: 24px; */
	font-size: 17px;
	margin: 8px 0;
}
.to-register {
	float: right;
	padding-right: 0;
}
.error-msg {
	/* 不设置block,margin不起作用 */
	display: block;
	float: right;
	color: #a94442;
	font-weight: bold;
}
.captcha {
	background-image: url(/account/captcha/generate);
	background-repeat: no-repeat;
	background-position: right;
	/* 如果不加!important,就无法生效 */
	padding-right: 120px !important;
}
.refresh {
	margin-left: 4px;
	cursor: pointer;
}
/* 复写.form-control的right属性,因为验证码输入框右端的提示图标遮挡住验证码 */
.captcha_icon {
	/* 如果不加!important,就无法生效 */
	right: 100px !important;
}

login.js(重头戏)

// 点击刷新图标,刷新验证码
function refreshCaptcha() {
	var url = '/account/captcha/generate?rand=' + Math.random();
	$('#captcha').css({
		'background-image': 'url(' + url + ')'
	});
	$('#captcha').val('');
	var parentDiv = $('#captcha').parents('div.has-feedback');
	var iconSpan = parentDiv.children('span.glyphicon');
	var msgSpan = parentDiv.children('span.error-msg');
	// 父亲div移除样式
	parentDiv.removeClass('has-success has-error');
	// 图标设置移除样式
	iconSpan.removeClass('glyphicon-ok glyphicon-remove');
	msgSpan.html('');
}

$(function() {
	// 登录表单验证
	$("#loginForm").validate({
		// 表单验证成功通过后的回调
		submitHandler: function(form) {
			//console.log(form);
			//form.submit();
			// 异步提交表单
			// Ajax提交数据
			var email = $('#email').val();
			var password = $('#password').val();
			var captcha = $('#captcha').val();
	        $.ajax({
	            url: '/account/login',    // 提交到controller的url路径
	            type: "POST",    // 提交方式
	            data: {
					email: email,
					password: password,
					captcha: captcha
				},  
	            dataType: "json",    // 服务器端返回的数据类型
	            success: function (res) {    
					if (res.code === 2000) {
						window.location.href = 'index.jsp';
					} else {
						alert('密码错误');
						location.reload();
					}
	            }
	        });
		},
		// 错误提示
		errorPlacement: function(error, element) { // 错误信息,input表单项
			// 找到父亲div
			var parentDiv = element.parents('div.has-feedback');
			// 找到显示msg的span
			var msgSpan = parentDiv.children('span.error-msg');
			// 找到图标的span
			var iconSpan = parentDiv.children('span.glyphicon');
			// 设置错误信息
			msgSpan.html('').append(error);
			// 父亲div添加样式has-error
			parentDiv.addClass('has-error');
			// 图标设置样式glyphicon-remove
			iconSpan.addClass('glyphicon-remove');
		},
		// 成功时,移除
		success: function(element) {
			// 找到父亲div
			var parentDiv = element.parents('div.has-feedback');
			// 找到显示msg的span
			var msgSpan = parentDiv.children('span.error-msg');
			// 找到图标的span
			var iconSpan = parentDiv.children('span.glyphicon');
			// 设置错误信息
			msgSpan.html('');
			// 父亲div移除样式has-error,并添加样式has-success
			parentDiv.removeClass('has-error');
			parentDiv.addClass('has-success');
			// 图标设置样式glyphicon-remove,并添加样式glyphicon-ok
			iconSpan.removeClass('glyphicon-remove');
			iconSpan.addClass('glyphicon-ok');
		},
		ignore: ".ignore",
		// 表单验证规则
		rules: {
			email: { // input的name属性
				required: true,
				email: true,
				//isMobile: true
				remote: {
					cache: false,
					async: true,
					type: 'GET',
					url: '/account/photo',
					data: { // 请求所需的参数列表
						email: function() {
							return $('#email').val();
						}
					},
					// 由于remote需要的返回值是布尔值(false表示不通过),而实际返回值是一个封装对象
					// 所以需要dataFilter对返回的封装对象进行预处理,并给remote返回所需的布尔值
					dataFilter: function(jsonStr, type) {
						var res = JSON.parse(jsonStr);
						
						console.log(typeof(res));
						console.log(res);
						console.log(res.code);
						
						var isOk = (res.code === 2000);
						console.log(isOk);
						// 如果email存在且data部分不为null(有头像),显示用户的头像
						if (isOk && res.data != null) {
							$('img.photo').attr('src', res.data);
						}
						return isOk;
					}
				}
			},
			password: {
				required: true
			},
			captcha: {
				required: true,
				remote: {
					cache: false,
					async: true,
					type: 'GET',
					url: '/account/captcha/validate',
					data: { // 提交给服务端的数据(键值对)
						captcha: function() {
							return $('#captcha').val();
						}
					},
					dataFilter: function(jsonStr, type) {
						var res = JSON.parse(jsonStr);
						var isCorrect = (res.code === 2000);
						if (!isCorrect) { // 验证码错误
							refreshCaptcha();
						}
						return isCorrect;
					}
				}
			}
		},
		messages: { // 与验证规则一一对应的消息提示
			email: {
				required: '邮箱不能为空',
				email: '邮箱格式错误',
				remote: '该邮箱尚未注册'
			},
			password: {
				required: "密码不能为空"
			},
			captcha: {
				required: "验证码不能为空",
				remote: "验证码错误,已经刷新"
			}
		},
		onkeyup: function(element, event) {
			var name = $(element).attr("name");
			if (name == "captcha") {
				//不可去除,当是验证码输入必须失去焦点才可以验证(错误刷新验证码)
				return false;
			}
		}
	});

	
});

猜你喜欢

转载自blog.csdn.net/qq_43290318/article/details/109661987