话说:

各位读者朋友,中午好!春节越来越近,归家心切丫!

前面一系列博客详细介绍了过程,但是没有复盘,这结尾的2篇博客就完成这件事。

这篇博客目标:优化小确幸论坛。
小确幸BBS论坛-1-5实现了最核心的功能,但是还有不少细节因为时间、技术原因暂时搁置了下来,现在的项目,比较满意。虽然很基础的东西,但是要考虑的细节还是蛮多。任何时候,多要尽力去做好,做到心中满意,就像那个毛毛虫睁眼看世界一样。
目录:


优化1:前端2大验证
1)注册验证;
2)登录验证;
优化2:异步搜索优化
优化3:流程优化
优化4:个人中心分页(包装类)
优化5:个人中心批量删除
优化6:消息中心
优化7:前端其他验证
优化8:前端小美化
总结:


难度系数:★★☆☆☆
所以,这篇还是侧重技术,而不是总结,下一篇大总结侧重总结。

优化1:前端2大验证
1)注册验证;
2)登录验证;

这2个的优化前期已经有了足够的铺垫;在我的前端1-5篇博客中都有影子,当时在外界界面实现的代码,这次是自己之前写的界面,更有归属感。代码还是粘一下,重点搞定了以下几个疑惑。

1.onsubmit()事件调用的方法,要写在原生的JS里面,写在JQuery初始化函数里没用的;

2.jQuery并不一定方便,比如checkbox的全选与反选,原生JS相当的简洁,所以2者搭配起来用,用的时候注意,jQuery精准选点很重要,jQuery里面多是方法:val().trim() html()等;js多是属性.value .html

3.外部引入的JS本质和内部写函数是一样的,所以内部的全局变量,外部JS可以直接用,这个在验证法是否正确,最终决定是否提交表单中,传递boolean类型的flag元素时候很有用!

4.前端和后端数据如何交互?Ajax;是不是一定要用Ajax呢?不一定。比如Session中的值,页面可以通过JSP方法放到一个隐藏表单中,然后提交到后台接收。方法灵活多样,随机应变即可。

1)注册验证;
注册效果是这样的:

这个验证也是分2步:一个是onsubmit()事件,这个是引入外部JS;一个是blur(),用jQuery写的,跟之前“套路”类似。
页面代码:

<form  name="regForm" action="addUser" method="get" onsubmit=" return checkRegisterForm()" >
<!-- border="1px solid red"  --><table  cellspacing="15" cellpadding="10" style="padding-left: 60px"><tr><!-- align="center" --><td colspan="2" style="font-size:30px;font-weight:bolder;color:green;padding-left:130px;">注册</td></tr><tr><td colspan="2"><span id="regInfo" style="color:red;font-size:15px;font-weight:bolder;"></span></td></tr><tr><td>用户名</td><td><input type="text" name="userName"  id="userName" class="myInput" placeholder="手机|邮箱|用户名" /><span id="userNameInfo"></span>  </td></tr><tr><td>昵称</td><td><input type="text" name="userNick"  id="userNick" class="myInput"><span id="userNickInfo"></span> </td></tr><tr><td>密码</td><td><input type="password" name="password" id="password" class="myInput"><span id="passwordInfo"></span>  </td></tr><tr><td>确认密码</td><td><input type="password" name="rePassword"  id="rePassword" class="myInput"><span id="rePasswordInfo"></span> </td></tr><tr><td>手机号</td><td><input type="text" name="phone" id="phone" class="myInput"><span id="phoneInfo"></span></td></tr><tr><td>邮箱</td><td><input type="text" name="email" id="email" class="myInput"><span id="emailInfo"></span></td></tr><tr><td>身份证号</td><td><input type="text" name="IDNumber"  id="IDNumber" class="myInput"><span id="IDNumberInfo"></span></td></tr><tr><td>性别</td><td><input type="radio" name="sex" value="男">男 &nbsp;&nbsp;&nbsp;<input type="radio" name="sex" value="女">女</td></tr><tr><td>爱好</td><td><input type="checkbox" name="hobby" value="篮球">篮球<input type="checkbox" name="hobby" value="游泳">游泳<input type="checkbox" name="bobby" value="象棋">象棋<input type="checkbox" name="bobby" value="钢琴">钢琴<input type="checkbox" name="hobby" value="蹦极">蹦极</td></tr><tr><td>省份</td><td><select name="province"><option value="" >选择省份</option><option value="陕西">陕西</option><option value="北京">北京</option><option value="广东">广东</option><option value="上海">上海</option></select></td></tr><tr><td>自我介绍</td><td><textarea name="introduce" placeholder="来段自我介绍呗..." cols="45" rows="3"></textarea></td></tr><tr><td colspan="2" align="center"><input type="submit" value="注册" style="width: 100px;height:30px;">&nbsp;&nbsp;&nbsp;<input type="reset" value="重置" style="width:100px;height:30px;"></td></tr></table></form>

内部jQuery:

<!-- 引入jQuery -->
<script src="js/jquery-1.8.3.js"></script><!-- 这里做鼠标移入 移除事件验证 -->
<script type="text/javascript">$(function() {$("#userName").focus();//1. 注册用户名Ajax异步验证$("#userName").blur(function() {var userName = $(this).val().trim();if(userName == "") {$("#userNameInfo").html("请填写用户名");$("#userNameInfo").css({"color":"red","font-size":"15px","font-weight":"bolder"});}else{$.ajax({url:"getUserByUserName",type:"get",dataType:"json",async:true,data:{"userName":userName},success:function(data) {console.log(data);if(data == "") {$("#userNameInfo").html("√");$("#userNameInfo").css({"color":"green","font-size":"25px","font-weight":"bolder"});}else{$("#userNameInfo").html("该用户已注册");$("#userNameInfo").css({"color":"red","font-size":"15px","font-weight":"bolder"});}},error:function() {alert("遗憾,请求失败!");}});}});//2.验证昵称$("#userNick").blur(function() {var userNick = $(this).val().trim();if(userNick == "") {$("#userNickInfo").html("请填写昵称");$("#userNickInfo").css({"color":"red","font-size":"15px","font-weight":"bolder"});}else{$("#userNickInfo").html("");}});//3.验证密码var password = "";$("#password").blur(function() {password = $(this).val().trim();if(password == "") {$("#passwordInfo").html("请填写密码");$("#passwordInfo").css({"color":"red","font-size":"15px","font-weight":"bolder"});}else{if(password.length<6) {$("#passwordInfo").html("密码至少6位数奥");$("#passwordInfo").css({"color":"red","font-size":"15px","font-weight":"bolder"});}else{$("#passwordInfo").html("");}}});//4.验证确认密码$("#rePassword").blur(function() {var rePassword = $(this).val().trim();if(rePassword == "") {$("#rePasswordInfo").html("请填写确认密码");$("#rePasswordInfo").css({"color":"red","font-size":"15px","font-weight":"bolder"});}else{if(password != rePassword) {$("#rePasswordInfo").html("前后密码不一致奥");$("#rePasswordInfo").css({"color":"red","font-size":"15px","font-weight":"bolder"});}else{$("#rePasswordInfo").html("");}}});//5.验证手机号$("#phone").blur(function() {var phone = $(this).val().trim();if(phone == "") {$("#phoneInfo").html("请填写手机号");$("#phoneInfo").css({"color":"red","font-size":"15px","font-weight":"bolder"});}else{var phoneReg = /^1[34578]\d{9}$/; if(phoneReg.test(phone)) {$("#phoneInfo").html("");}else{$("#phoneInfo").html("邮箱非法");}}});//6.验证邮箱$("#email").blur(function() {var email = $(this).val().trim();if(email == "") {$("#emailInfo").html("请填写邮箱");$("#emailInfo").css({"color":"red","font-size":"15px","font-weight":"bolder"});}else{var emailReg = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/;if(emailReg.test(email)) {$("#emailInfo").html("");}else{$("#emailInfo").html("邮箱非法");$("#emailInfo").css({"color":"red","font-size":"15px","font-weight":"bolder"});}}});//7.验证身份证号$("#IDNumber").blur(function() {var IDNumber = $(this).val().trim();if(IDNumber == ""){$("#IDNumberInfo").html("请填写身份证号");$("#IDNumberInfo").css({"color":"red","font-size":"15px","font-weight":"bolder"});}else{var idReg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/; if(idReg.test(IDNumber)) {$("#IDNumberInfo").html("");}else{$("#IDNumberInfo").html("身份证号有误");$("#IDNumberInfo").css({"color":"red","font-size":"15px","font-weight":"bolder"});}}});})</script>

外部JS:

function checkRegisterForm() {//alert("进来了!");var regForm = document.regForm;var userName = regForm.userName.value;var userNick = regForm.userNick.value;var password = regForm.password.value;var rePassword = regForm.rePassword.value;var phone = regForm.phone.value;var email = regForm.email.value;var IDNumber = regForm.IDNumber.value;var sex = regForm.sex.value;var hobby = regForm.hobby;var province = regForm.province.value;var introduce = regForm.introduce.value;//alert(introduce);//获取提示信息span对象var regInfo = document.getElementById("regInfo");if(userName == "") {regInfo.innerHTML = "请填写【用户名】:";regForm.userName.focus();return false;}else if(userNick == "") {regInfo.innerHTML = "请填写【昵称】";regForm.userNick.focus();return false;} else if(password == "") {regInfo.innerHTML = "请填写【密码】";regForm.password.focus();return false;}else if(rePassword == "") {regInfo.innerHTML = "请【确认密码】";regForm.rePassword.focus();return false;}else if(phone == "") {regInfo.innerHTML = "请填写【手机号】";regForm.phone.focus();return false;}else if(email == "") {regInfo.innerHTML = "请填写【邮箱】";regForm.email.focus();return false;}else if(IDNumber == "") {regInfo.innerHTML = "请输入【身份证号】";regForm.IDNumber.focus();return false;}else if(sex == "") {regInfo.innerHTML = "请选择【性别】";//regForm.sex.focus();//这里千万不能聚焦!return false;}else if(!checkHobby()) {regInfo.innerHTML = "请选择【爱好】";//regForm.hobby.focus();//这里也类似//alert(hobby);return false;}else if(province == "") {regInfo.innerHTML = "请选择【省份】";return false;}else if(introduce == "") {regInfo.innerHTML = "请填写【自我介绍】";regForm.introduce.focus();return false;}else {return true;}/**if(select.selectedIndex ==0){alet("进来了");regInfo.innerHTML = "请选择【省份】";//存在的问题是没法验证省份是否选择,总是进不了选择语句!return false;}*/}//判断复选框   要知道爱好是个数组,不能直接.value的来判断啊  纠结了近1个多小时!
function  checkHobby() {var regForm = document.regForm;var hobby = regForm.hobby;var flag = false;for(var i=0;i<hobby.length;i++) {if(hobby[i].checked){flag = true;break;}else {}}return flag;
}

当然,现在的注册已经简化到一个手机号就搞定!要是再这样注册,用户岂不疯掉?这个主要是熟练各种表单元素罢了。

2)登录验证;

登录稍微复杂了点点,涉及到了cookie,这个之前已经实现,这次主要加入的是Ajax异步验证。

login.jsp

<!-- 登录首页 -->
<div id="loginForm"><form  name="loginForm" action="login" method="get"  onsubmit="return checkLoginForm(flag)"><!-- onsubmit="return checkLoginForm()" --><table cellspacing="20"><tr><td colspan="2"  align="center" style="font-size:25px;font-weight:bolder;color:green">登录</td><span><a href="register.jsp">还没有账号?立即注册</a></span></tr><tr><td colspan="2"><span id="loginInfo" style="color:red;font-size:15px;font-weight:bolder;">${msg}</span></td></tr><tr><td align="center">用户名</td><td><input type="text" name="userName" id="userName" class="myInput" value="<%=userName %>"></td><td><span id="userNameInfo"></span></td></tr><tr><td align="center">密&nbsp;&nbsp;&nbsp;码</td><td><input type="password" name="password" id="password" class="myInput" value="<%=password %>" ></td></tr><tr><td align="center">验证码</td><td><input type="text" name="code"  id="code" class="myInput" maxlength="4" style="width:90px;"><img src="vertifyCode" onclick="this.src='vertifyCode?'+new Date()" style="float:right;margin:right:10px;width:90px;height:35px;"><span id="codeInfo"></span></td></tr><tr><td colspan="2" align="center"><input   id="remember" name="remember" value="1"  type="checkbox" style="width: 18px;height: 18px;">&nbsp;&nbsp;记住我? &nbsp;&nbsp;&nbsp;<a href="#">忘记密码?</a></td></tr><tr><td colspan="2" align="center"><input type="submit" value="登录"  id="sub" style="width: 150px;height: 35px;"></td></tr>  </table></form>
</div>

jQuery

<!--引入jQuery  -->
<script src="js/jquery-1.8.3.js"></script><!-- 前端Ajax验证用户名 -->
<script type="text/javascript">
var flag = true;$(function() {$("#userName").focus();//用户名异步验证$("#userName").blur(function() {//alert("失去焦点!");//获取userName的值  对用户名进行非空验证var userName = $(this).val().trim();if(userName == "" ||userName == null){$("#loginInfo").html("请输入用户名");$("#loginInfo").css({"color":"red","font-size":"15px","font-weight":"bolder"});}else{$("#loginInfo").html("");}//Ajax异步验证用户名是否存在?$.ajax({url:"getUserByUserName",//访问的Servlet或者Controller方法async:true,//是否异步type:"get",//postdata:{"userName":userName},//往Servlet或者 Controller发送数据success:function(data){//回调函数console.log("进来了!");console.log(data);//Object {userId: 4, userName: "admin2", userNick: "不三不四", password: "222222", rePassword: "222222"…} 如果没有数据:[]var userNameInfo = ""; //定义用户名提示信息if(data!="") {console.log("用户存在");userNameInfo = "√";$("#userNameInfo").html(userNameInfo);$("#userNameInfo").css({"color":"green","font-size":"25px","font-weight":"bolder"});}else{console.log("用户不存在!");userNameInfo = "×";$("#userNameInfo").html(userNameInfo);$("#userNameInfo").css({"color":"red","font-size":"25px","font-weight":"bolder"});//$("#userName").focus();//聚焦直到输入正确为止}},error:function(){alert("报错啦!");},dataType:"json"//传送的数据类型});});//密码验证$("#password").blur(function() {//alert("进来了!");var password = $(this).val().trim();if(password =="") {$("#loginInfo").html("请填写密码");//$(this).focus();}else{$("#loginInfo").html("");}});//验证码验证$("#code").keyup(function(){//alert("进来了!");var code = $(this).val().trim();var code2 = document.getElementById("code").value.toLowerCase();$.ajax({url:"ajaxCode",async:true,type:"get",dataType:"json",success:function(data) {console.log("后台传过来的验证码:"+data);console.log("前台接收的输入的验证码:"+code2);if(code2.length==4) {if(data  == code2) {console.log("一致");$("#codeInfo").html("√");$("#codeInfo").css({"color":"green","font-size":"25px","font-weight":"bolder"});flag = true;}if(data != code2) {$("#codeInfo").html("×");//此处提示验证码有误!$("#codeInfo").css({"color":"red","font-size":"25px","font-weight":"bolder"});flag = false;} }if(code.length<4){flag = false;}},error:function() {console.log("验证码失败!");}});});//验证码blur事件验证$("#code").blur(function() {var code = $(this).val().trim();if(code == ""){$("#loginInfo").html("请填写验证码");//$(this).focus();}else{$("#loginInfo").html("");}});})</script>

外部js

<!--引入外部JS  -->
<script type="text/javascript" src="js/login.js"></script> //登录简单验证
function checkLoginForm(flag) {//alert(flag); flag作用是验证码一旦错误,就不能提交//获取表单元素var lgForm = document.loginForm;var userName = lgForm.userName.value;var password = lgForm.password.value;var code = lgForm.code.value;var lgInfo = document.getElementById("loginInfo");if(userName == "") {//alert("用户名为空!");lgInfo.innerHTML = "请填写【用户名】";lgForm.userName.focus();return false;}else if(password == "") {lgInfo.innerHTML = "请填写【密码】";lgForm.password.focus();return false;}else if(code == "") {lgInfo.innerHTML = "请填写【验证码】";lgForm.code.focus();return false;}else {lgInfo.innerHTML = "";//alert(flag+"  "+code.length);if(code.length<4){lgInfo.innerHTML = "验证码不正确";}return flag;}}

方法类似,这是适合我的方法和思路,诸位也许有更简洁的方法。用外部已经封装好的插件,也是不错的选择。我主要还是想掌控一下过程。
还有cookie

后台response添加cookie.原理之前博客细说过,不在赘述,前台接收下。
<%String userName  = "";String password = "";Cookie[] cookies =   request.getCookies();//判断是否为null这一步很关键,如果没有判断,第一次访问没有任何cookie,就报错NullPointException 第二次访问,才会访问到。if(cookies!= null) {for(int i=0;i<cookies.length;i++) {if(cookies[i].getName().equals("userName")) {userName =    cookies[i].getValue();System.out.println("cookie里面的用户名:"+userName);}if(cookies[i].getName().equals("password")) {password = cookies[i].getValue();System.out.println("cookie里面的密码:"+password);}}}%>
页面取出来即可。
<tr><td align="center">用户名</td><td><input type="text" name="userName" id="userName" class="myInput" value="<%=userName %>"></td><td><span id="userNameInfo"></span></td></tr>

优化2:异步搜索优化

效果图:

之前 ,我们实现的效果是,不同栏目下搜索内容是不一样的,在各栏目下,搜索不到全局内容,因为搜索是模糊匹配+栏目Id。这次直接实现动态提示效果。技术:Ajax
1)从后台取数据;写入div;
2) 写3个事件 onclick() onmouseover() onmouseout()即可

1)查数据

<script type="text/javascript">$(function() {//搜索框Ajax提示$("#searchAjax").keyup(function() {console.log("进来了...");var searchContent = $(this).val().trim();console.log("搜索内容是:"+searchContent);if(searchContent != null) {var content = "";$.ajax({url:"searchAjax",type:"get",dataType:"json",async:true,data:{"search":searchContent},success:function(data) {console.log("请求后台成功!");console.log("后台传过来的data:"+data);console.log("++++++++++++++++++++++++++++++++++++++");for(var i = 0;i<data.length;i++) {console.log(data[i].title);content +="<div style='height:30px;cursor:pointer;' onclick='myClick(this)'  onmouseover = 'myOver(this)'  onmouseout='myOut(this)'>"+data[i].title+"</div>"}  $("#showDiv").html(content);$("#showDiv").css({"width":"470px","background-color":"rgb(249,249,249)","opacity":"0.6","color":"black"});//超链接块儿$("#showDiv").css("display","block");},error:function() {console.log("请求后台失败!");}});}}); })/* 写搜索样式 *///点击  把内容写入搜索框function myClick(obj) {//alert("进来了!");var searchAjax = document.getElementById("searchAjax");//console.log(obj.innerHTML);//alert(obj.innerHTML);searchAjax.value= obj.innerHTML;}//鼠标悬停  样式function myOver(obj) {console.log("移入啦..");obj.style.backgroundColor= "orange";}function myOut(obj) {console.log("移出啦。。");obj.style.backgroundColor = "";}</script>

2)前端

<!--搜索框的div  -->
<div id="search" style="margin: -70px 0px 0px 450px;width:500px;">
<form action="textShow" method="get">       <input type="text" name="search" style="width:400px;height:40px;" id="searchAjax"/><input type="submit" value="搜索一下" style="width:60px;height:40px;"><div id="showDiv"></div><input type="hidden" name="categoryId" value="${categoryId}">
</form>
</div>

核心:就是从后端来个模糊查询,Ajax传到前端,然后放到div中,设置不同事件,达到预期效果。看起来,寥寥几笔,还是有不少细节需要注意的呢。

优化3:流程优化

当时我发布的博客1-5中,大体实现了主体功能,并未过多考虑流程,脑子中知道,未来得及实现。
用户未登录,可以直接看帖子,但是不能发帖,也没有个人中心;发帖提示登录;登录后显示个人中心,并可以发帖。逻辑很简单,具体实现在前端其他验证部分细讲。
未登录效果:

登录后效果:

优化4:个人中心分页(包装类)
这个分页,自己专门发表过2篇博客,1篇是用Servlet中直接写分页5要素的,1篇是封装成了包装类。这里体现的特别好,因为多处都要分页:首页要分;每个栏目要分;个人中心要分…一旦多起来,封装的思想就有了用武之地!

当时用Servlet中直接写的,这里个人中心用的包装类。
具体实现,不在赘述,粘贴一下页面代码,

用到了<c:choose><c:when><c:otherwise>他们是一体的,不能拆开用。当时只用了<c:when><c:otherwise>报错啦。

未用这组标签之前,是这么做的:

<!--遍历textList取值  --><c:if test="${empty textList}"><tr><td colspan="4" style="color:red;font-weight:bolder;" align="center">It's a pity,未查询到数据奥~~</td></tr>
</c:if><c:if test="${!empty textList}"><c:forEach var="ListShow" items="${textList}"><tr><td><a href="textDetail?textId=${ListShow.textId}">${ListShow.title }</a></td><td align="center"><%-- <a href="#">${ListShow.user.userNick}</a> --%>${ListShow.user.userNick}</td><td align="center">${ListShow.replyCount }</td><td align="center">${ListShow.textTime}</td></tr></c:forEach>
</c:if>
判断了2遍,都用<c:if>判断为空否,用empty

用了标签后,是这么做的:

<c:choose><c:when test="${not empty  msgInfoList}"><c:forEach var="msgInfo" items="${msgInfoList}"><tr align="center"><td>${msgInfo.msgId}</td><td>${msgInfo.userId}</td><td>${msgInfo.textId }</td><td>${msgInfo.msgCount}</td></tr></c:forEach></c:when><c:otherwise><tr><td colspan="4" align="center" style="color:red;font-weight:bolder;">暂无消息奥~~~</td></tr></c:otherwise></c:choose>

优化5:个人中心批量删除
这个当时完全是为了好玩,也增加了样式。效果如下:

因为这个bbs核心技术是;Servlet+JSP,所以这里介绍2种方式实现,
法1:如果是Servlet+JSP,那么前端获取到数组,然后遍历写SQL删除即可delete from text where textId in (???),
法2:如果是在框架中,直接扔进去一个字符串数组,通过Mybatis的集合SQL删除即可。

全选与反选,小美前面专门有1篇博客,通过JS和jQuery2种方式都实现了的。
如有需要,请翻出来瞅瞅哈。

法1:
前端:

<th>全选                 <form action="delAll" method="post" onsubmit="return checkDelTexts()"><input type="checkbox" id="delAll" value="0" class="myCheckbox"  onclick="checkAll(this)"><!-- 把当前页信息传过去 --><input type="hidden" name="pageIndex" value="${pager.pageIndex}"></th>
<th><input type="submit" value="全删 " style="width:30px;height:35px;">
</th>
<!--批量删除  -->
<td align="center" colspan="2"><input type="checkbox"  name="chk" value="${listUserInfo.text.textId}" class="myCheckbox" >
</td>

后端Servlet:

package com.hmc.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.hmc.service.UserRegisterService;
import com.hmc.service.UserRegisterServiceImpl;
import com.hmc.util.GetStrToInt;
import com.hmc.util.Pager;/**
*
*2018年1月19日
*User:Meice
*上午2:11:17
*/
public class DelAllServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("全选删除进来了!");//1.接收参数String[] checkArray =    req.getParameterValues("chk");//接收到当前页,便于批量删除后的跳转String pageIndexStr =    req.getParameter("pageIndex");int pageIndex = GetStrToInt.getInt(pageIndexStr);System.out.println("批量删除接收到的当前页:*******************   "+pageIndex);for(String str:checkArray) {//System.out.print(str+"===");}//System.out.println("待删除帖子Id为:"+checkArray);//2.调用方法UserRegisterService urs = new UserRegisterServiceImpl();for(int i=0;i<checkArray.length;i++) {String sql = "delete from text where textId = ?";int textId = GetStrToInt.getInt(checkArray[i]);Object[] params = {textId};urs.Cud(sql, params);}req.setAttribute("pageIndex", pageIndex);//3.跳转页面req.getRequestDispatcher("userInfo").forward(req, resp);}
}

这里也没什么,需要注意的是,删除的时候,最终页面跳转要注意一下,不能当前页删除,跳转到了其他页!所以删除时候,要想办法把当前页(pageIndex)传过去。这里采用的隐藏域。包括修改也是一样,修改先跳转到修改页面,要把pageIndex传到页面,页面也要通过隐藏域提交,后天接收处理。只要有了pageIndex,就能保持当前页。

之所以会跳转到第一页,是因为分页的时候,参数pager.pageIndex肯定<0;而我们在set()方法里面做了判断,if(pageIndex<0){pageIndex = 1;},因此关键在于删除的时候没有传当前页参数(pageIndex),只是传了textId.

对症下药:传2个参数:textId pageIndex 分页类自然会根据传过来的参数跳转喽。
1)单个删除可以直接传参:
注意如何传递多个参数,跟以前一样。

 <a href="delText?textId=${listUserInfo.text.textId}&pageIndex=${pager.pageIndex}"  onclick="return confirm('真的忍心删除我麽?')">删除</a>

2)批量删除:就没这么简单,因为批量删除是checkbox 根据name=“chk” value=“textId” 根据name得到的是textId数组,在框架里面,直接给一个array类型的集合参数,直接干掉。所以没发直接给value带参?怎么办?
批量删除,如果是全选,当然是只删除当前页,所以跳转哪一页还不好定,可以默认跳转当前页;如果多个删除,那么就应该像当个删除那样,跳转至当前页;这么办:页面个人中心必定有当前页pageIndex,它是pager的一个参数,作为隐藏域传过去。在处理批量删除的Servlet中(DelAllServlet)中,接收到后,批量删除处理完毕,在继续传过去
(req.setAttribute(“pageIndex”,pageIndex);因为request请求只在一次请求中有效,所以需要不断传递。搞定!同样的道理,修改这么做:需要注意一下,跳转有2个环节,先跳转修改页面,然后执行修改操作,所以参数要传递2次。跳转到修改页面后,页面接收Servlet传递过来的参数,以隐藏域方式继续传过去。

 1)<a href="updateText?textId=${listUserInfo.text.textId}&pageIndex=${pager.pageIndex}" style="color:green;font-weight:bolder">修改</a> 传值过去2)接收:String pageIndexStr = req.getParameter("pageIndex");int pageIndex = GetStrToInt.getInt(pageIndexStr);3)赋值req.setAttribute("pageIndex", pageIndex);

下一步就到了我们分页界面,pageIndex是从页面接收的,如果没给,默认为0,我们通通变化为了1.
整体结论:一旦涉及分页,就要考虑多一点,无论修改还是删除,我们需要传递2个参数:1是被删除对象的当前Id;而是被删除对象所在当前页参数pageIndex,避免跳转页面天马行空!
以上是我优化时候的小结,有点啰嗦。

法2:如果是框架呢?框架中批量删除核心在于Mybatis的SQL语句中的集合。
当时我是在日志管理的时候,做了个批量删除,当然实际工作中没这个需求,只是好玩。

这里面核心是Mapper.xml和Controller。Controller思想和Servlet+JSP差不多,关键还是mapper.xml要注意下:
mapper.xml

<!--3.删除日志  --><delete id="delLog" parameterType="Integer">delete from log where logId = #{logId}
</delete> <delete id="delLogs" parameterType="String">delete from log where logIdin <foreach collection="array"  item="logId" open="("    close=")"     separator=",">#{logId}</foreach>
</delete>

String[]数组,参数类型parameterType就是String即可。遍历只不过把我们原来写在Servlet中的for()循环写到了SQL语句中罢了。因为Id是个数组,所以array;item和#{logId}中的保持一致即可,就是那个数组取个名字罢了,然后就是拼凑成SQL的语法格式啦。

Controller这么写:

//单个 批量删除日志法2 @RequestMapping("delLogs")public  String delLogs(HttpServletRequest req) {String[] logIds = req.getParameterValues("chk");System.out.println("调用删除数组logId方法");if (logIds != null) {logService.delLogs(logIds);}return "redirect:logList";}

方法里面虽然不用循环了,但是SQL语句繁琐了,还要记一堆语法,各有利弊。

还没有结束,还有前端选中后的效果呢:

<!--隔行换色 与鼠标悬停  -->
<script type="text/javascript">$(function() {/* 批量删除  每行 添加背景色  搞不定,用jQuery*/$("#myText table tbody input:checkbox[name='chk']").click(function() {//alert("进来了!");var chk = $(this).attr("checked");//alert(chk=="checked");if(chk == "checked") {$(this).parents("tr").addClass("myDelAll");}else{$(this).parents("tr").removeClass("myDelAll");}});//全选也做一下 不然三不像$("#delAll").click(function() {//alert("进来了!");if($(this).attr("checked")=="checked") {$("#myText table tbody input:checkbox[name='chk']").each(function() {$(this).parents("tr").addClass("myDelAll");});}else{$("#myText table tbody input:checkbox[name='chk']").each(function() {$(this).parents("tr").removeClass("myDelAll");});}});})</script>

这里麻烦在不能精准选择,或者属性容易混淆,尤其选中,变色,再次点击,取消,我们第一反应应该是toggle()事件,然而,这里必须用click()
Why?
checkbox的toggle事件与checkbox的选中状态会冲突。怎么破?
问题背景:批量删除时候,想实现这样的效果:选中的行加个样式(checkbox)
因为需要事件,一开始想到的是toggle,因为可以直接写2个函数,但是 结果呢?checkbox不能选中了!
The implementation also calls .preventDefault() on the event, so links will not be followed and buttons will not be clicked if.toggle() has been called on the element.因为被阻止冒泡了,用toggle的时候checkbox会失效。
toggle方法中阻止了缺省动作
jquery原码中toggle在注册click时调用了event.preventDefault();

优化6:消息中心

如果给用户回帖了,那么消息中心有提示,打开后是这个样子,一旦返回,消息中心自动清空。

当然消息中心有点简陋,但是就是这么个意思,用消息队列来做,据说很方便。
这里要处理的核心就是不同用户要显示自己的消息——多少人给我帖子回复了。所以,我的解决思路是这样的:需要知道每个用户帖子的回复数量。在别人回帖之前查一次,回帖之后查一次;需要知道哪个用户;他名下所有帖子回复数量,我就新创了一个表,两次查询之差都是1,然后把用户Id、帖子Id、数量1存到表中,最后用count()数量即可。

最后要显示多少,直接重新查,并且统计一次即可。
select count(msgCount) from msg where userId = ?
然后就页面通过各种途径查,然后取出来即可。

这个还是费了点心思的。

优化7:前端其他验证

这里比较琐碎:


1)用户未登录,要发帖,提示登录否?
2)登录后发帖,验证;
3)批量删除验证;


1)用户未登录,要评论,提示登录否?

这里关键在于页面跳转,之前的思维是通过response.getWriter().write()中写js来实现,但是从来没有效果,这次变化了思维,直接前端来。
前端验证要搞定一个问题,那就是怎么判断用户是否已经登录?用户登录否,session知道,session中数据前端怎么获取?隐藏域!

<form action="addReply"  method="get" onsubmit="return checkAddReplyForm()"><input type="hidden" name="textId" value="${textAll.text.textId}"><!-- 隐藏域,便于前端判断 --><input type="hidden" id="user" value="${user}"><textarea  name= "replyContext" cols="140" rows="10" style="resize:none;" id="reply"></textarea><br/>
<input type="submit" value="评论" style="width:100px;height:40px;margin-left:900px;">
</form>

在调用后台之前,先判断下:

<!-- 评论验证 -->
<script type="text/javascript">function checkAddReplyForm() {//alert("进来了!");var flag = false;var reply =   document.getElementById("reply");var replyVal = reply.value.trim();//获取用户 隐藏域var user = document.getElementById("user");var userVal = user.value;//alert(userVal);//用户不存在,评论与否都不重要了if(userVal == "") {//alert("您未登录,请登录后在评论奥 *.* "); 这里不要直接跳转,很唐突,让用户选择var sure = window.confirm("登录后方能评论奥?要登录么?");if(sure) {flag = true;}else{flag = false;}}else{if(replyVal == "") {alert("评论空空如也~");}else if(replyVal.length<10) {alert("评论量太少奥~");}else{flag = true;}}return flag;}</script>

这样就完美的处理了判断呢用户是否登录的问题。
评论非空,要求10个字符以上。

2)登录后发帖,验证

先看前端:

<div id="addText" style="margin:10px 0px 0px 160px;"><form action="addText" method=-"get" onsubmit=" return checkAddTextForm()"><table><tr><td>标题:</td><td><input type="text" name="title" id="title" style="width:600px;height:30px;"></td><td><select name="category" style="width:100px;height:30px;" id="category"><option value="0">选择栏目</option><c:forEach var="category" items="${categoryList}"><option value="${category.categoryId}">${category.categoryName}</option></c:forEach></select></td></tr><tr><td style="text-align: inherit;">内容:</td></tr><tr><td colspan="3"><textarea name="context" cols="150" rows="20" id="textContent"></textarea></td></tr><tr><td colspan="3" align="right"> <input type="submit" value="发布" style="width:100px;height:30px;"></td></tr></table></form></div>

验证:

<!-- 验证发帖  -->
<script type="text/javascript">function checkAddTextForm () {//alert("进来了!");var flag = false;//验证帖子标题var title = document.getElementById("title");var titleVal = title.value.trim();//验证栏目var category = document.getElementById("category");var categoryVal = category.value;//验证内容var content = document.getElementById("textContent");var contentVal = content.value.trim();//alert(titleVal);if(titleVal == "") {alert("填写标题奥~");}else if(categoryVal == 0) {alert("请选择栏目");}else if(contentVal == "") {alert("不要忘了发帖内容奥~~");}else if(contentVal.length<10) {alert("发帖内容也忒少了吧~~~");}else{flag = true;}return flag;}</script>

这样简单的验证,直接原生JS,都不用导包。

3)批量删除验证;

批量删除验证,要注意下,你怎么判断是否选择?只要有1个选择,就可以提交,对吧,反向思维。

/*批量删除前端验证  */function checkDelTexts() {var  texts =    document.getElementsByName("chk");var flag = false;for(var i=0;i<texts.length;i++) {if(texts[i].checked == true) {flag = true;}}if(flag == false) {alert("请先选择,后删除奥~");}return flag;}

优化8:前端小美化

1.选中当前栏目,当前栏目增加样式
2.隔行变色,鼠标移入移除增加样式
看起来很简单吧,也需要注意下小细节。

1.选中当前栏目,当前栏目增加样式

我当时也是直接给栏目写click()事件,但是因为点击后,页面在后台跳转刷新,效果就瞬间显示,所以怎么办?只好通过JSTL标签前端根据categoryId控制,于是有了这样一长串看似很冗余的代码:

<!--栏目div  --><div id="category" style="font-family:monospace;"><ul><!-- 前端不好,只能这样变化栏目背景色 --><c:if test="${categoryId == 0}"><li style="background-color:rgb(104,189,69);"><a href="textShow">首页</a></li></c:if><c:if test="${categoryId != 0}"><li><a href="textShow">首页</a></li></c:if><c:if test="${categoryId ==  1 }"><li id="liTwo" style="background-color:rgb(104,189,69);">   <a href="textShow?categoryId=1">Java</a></li></c:if><c:if test="${categoryId !=  1 }"><li id="liTwo" >   <a href="textShow?categoryId=1">Java</a></li></c:if><c:if test="${categoryId ==2 }"><li style="background-color:rgb(104,189,69);"><a href="textShow?categoryId=2">MySQL</a></li></c:if><c:if test="${categoryId !=2 }"><li><a href="textShow?categoryId=2">MySQL</a></li></c:if><c:if test="${categoryId ==3 }"><li style="background-color:rgb(104,189,69);"><a href="textShow?categoryId=3">大数据</a></li></c:if><c:if test="${categoryId !=3 }"><li><a href="textShow?categoryId=3">大数据</a></li></c:if><c:if test="${categoryId ==4 }"><li style="background-color:rgb(104,189,69);"><a href="textShow?categoryId=4">人工智能</a></li></c:if><c:if test="${categoryId !=4 }"><li><a href="textShow?categoryId=4">人工智能</a></li></c:if><c:if test="${categoryId ==5 }"><li style="background-color:rgb(104,189,69);"><a href="textShow?categoryId=5">HTML5</a></li></c:if><c:if test="${categoryId !=5 }"><li><a href="textShow?categoryId=5">HTML5</a></li></c:if><c:if test="${categoryId ==6 }"><li style="background-color:rgb(104,189,69);"><a href="textShow?categoryId=6">生活</a></li></c:if><c:if test="${categoryId !=6 }"><li><a href="textShow?categoryId=6">生活</a></li></c:if><c:if test="${categoryId ==7 }"><li style="background-color:rgb(104,189,69);"><a href="textShow?categoryId=7">有趣有料</a></li></c:if><c:if test="${categoryId !=7 }"><li><a href="textShow?categoryId=7">有趣有料</a></li></c:if></ul></div>

你有更好的办法么?

2.隔行变色,鼠标移入移除增加样式

这个就很简单啦。不过也需要注意,隔行换色和鼠标移入移除事件:
深刻体会.css();直接赋予样式和addClass()和removeClass()的本质区别。
遇到问题:隔行换色简单,鼠标移入、移除换个背景色也简单;问题是:鼠标移入、移除后把我隔行换色的效果也替换了…
原因也很简单:不应该替换背景色,而应该追加背景色。也就是在原有隔行换色的基础上追加样式、在去除样式即可。
而且,样式不要用.css()的方式写死,这样的话,背景色是没有变化的,因为写固定了。不会再原来的基础上追加或移除。
应该这么做:

<!-- 隔行换色 -->
<script type="text/javascript">$(function() {//隔行换色$("#textList table tbody tr:not(:last):odd").addClass("myOddRow");//鼠标悬停 隔行换色$("#textList table tbody tr:not(:last)").mouseover(function() {$(this).addClass("myRow");}).mouseout(function() {$(this).removeClass("myRow");});
}

因为是连贯的,所以没有拆开,但是这篇博客确实长了点,哈哈。很有含金量吧。

总结:


1.要有追求完美的心态,技术上实现不了,心态得有,总有一天可以搞定;

2.不要放弃暂时实现不了的功能,正如不要放弃梦想一样;放弃总是轻松的;

3.高级表格要多用。好处并不在于结构多么高级,而在于如果设置样式,非常便于选择。不用排除:first :last这种情况,可以根据thead tbody tfoot进行精准选择。
比如:

//鼠标悬停事件$("#myText table tbody tr").mouseover(function() {$(this).addClass("myRow");}).mouseout(function() {$(this).removeClass("myRow");});

4.css默认样式中这个字体类似:微软雅黑:

<div id="category" style="font-family:monospace;">

5.参数传递问题?
1.后台代码与前台代码如何传参?
应用场景:
1)验证码是后台生成的,前端 校验要从后台取数据;
2)注册、登录用户名的异步校验
Ajax传参
2.外部js与内部JS以及onsubmit()如何传参?
应用场景:如果引用了外部的js,内部也写了js jQuery,那么验证码一旦错误,需要给外部引入的js函数也就是
onsubmit(“return checkLoginForm()”) form表单最终提交与否,最后一步是要根据这个验证结果来判断的,所以
直接给方法带参即可:onsubmit(“return checkLoginForm(flag)”)

<script type="text/javascript">  var flag = false;</script>
     因为引用外部js本质还是在
<script type="text/javascript"></script>

里面直接写方法,所以声明的全局变量内外部都可以引用。这一点,一定要深刻体会!这就是为什么外部文件中直接可以写方法的原因!你会发现,后台方法还不是参数如何传递,方法返回值如何处理之类的事情。

6.checkbox的toggle事件与checkbox的选中状态会冲突。怎么破?
因为需要事件,一开始想到的是toggle,因为可以直接写2个函数,但是 结果呢?checkbox不能选中了!
The implementation also calls .preventDefault() on the event, so links will not be followed and buttons will not be clicked if.toggle() has been called on the element.因为被阻止冒泡了,用toggle的时候checkbox会失效。
toggle方法中阻止了缺省动作 jquery原码中toggle在注册click时调用了event.preventDefault();还是写click()事件,用if()判断是否checked == “checked”


哈哈,下一篇博客来个大总结,这个bbs,就告一段落啦。虽然是入门级别的,用心做好,还是蛮费心血的。

我担心的是今天博客发表得出去吗?太长了吧~~~ 再会!

小确幸BBS论坛-6-优化不止步!相关推荐

  1. 小确幸BBS论坛-1-前期准备

    话说: 各位读者晚上好!终于到了2018!2018年的第一篇博客哈!前面中断了不少篇,并非作者怠惰了,确实感觉"拿不出手",嘿嘿.经过一段时间积累,这次就连续的发表一个系列吧--B ...

  2. 小确幸BBS论坛-3-个人中心

    话说: 各位读者朋友,晚上好!前面介绍了注册登录.首页,本篇介绍个人中心 难度系数:★★☆☆☆ 目录 1.页面展示 2.个人中心pojo 3.个人中心帖子列表.发帖.修改.删除 1.页面展示 user ...

  3. 冬日里的小确幸无非是美味在旁

    你的生活是怎样的呢?每个人可能都会有不同的回答,对待生活的方式也不近相同.一日三餐,对于奔波的人可能仔细地去运作完成. 美食,在很大程度上给予了我们幸福感,也决定了你的生活态度. 冬日里不管是早起的你 ...

  4. 双11:天猫要“软着陆”,商家要“小确幸”

    世事如棋局局新,关关难过关关过.今年的双11,还是有不少变化的. 比如,满减的门槛降低.双11的满减变化,其实可以解读很多信息.2019年以及之前是"满400减50",2020年是 ...

  5. 计算机课堂热身游戏,童年小确幸 儿时电脑课里我们玩不腻的13个小游戏

    原标题:童年小确幸 儿时电脑课里我们玩不腻的13个小游戏 还记得小时候电脑课一到教室就是先开史莱姆之家下载小游戏到电脑裡来玩,因为那时候老师根本不给你网络上网, 但是那时候的小孩都很知足,只要随便一个 ...

  6. 华为盒子显示服务器繁忙,繁忙都市中的“小确幸”

    随着经济和网络的不断发展,现代人的生活被不断挤压,做不完的工作,赶不完的应酬和酒局,在繁忙的都市中似乎永远都慢不下来.走在下班回家的路上,想想自己已经有多久不曾好好读书:有多久没有完整看一部电影:又有 ...

  7. 飞利浦css5530+g评测,影院不开门,给生活多点小确幸,飞利浦CSS5235家庭影院开箱评测...

    原标题:影院不开门,给生活多点小确幸,飞利浦CSS5235家庭影院开箱评测 电影院已经停摆半年了,最后一次买电影票还是春节档,只是被自动退票了.最近的电影要么在电视上看,要么在电脑上看,视觉效果无法改 ...

  8. 小确幸与大抱负:为何自拍类APP成为新宠?

    新春伊始,自拍美图类APP成了香饽饽. 专注于自拍P图的美图,于2017年登陆港股,目前市值达400多亿港元.就在近日,消息称专注于自拍P图的Faceu激萌被今日头条以3亿美金收购.无独有偶,早在今年 ...

  9. 小时候计算机课玩的那个兔子的游戏是什么,童年小确幸 儿时电脑课里我们玩不腻的13个小游戏...

    还记得小时候电脑课一到教室就是先开史莱姆之家下载小游戏到电脑裡来玩,因为那时候老师根本不给你网络上网, 但是那时候的小孩都很知足,只要随便一个小游戏都可以让人玩一个下午. 有时候懒的下载还会直接存在3 ...

最新文章

  1. 模拟信号与数字信号以及数模隔离简介
  2. 什么是联机分析处理(OLAP)
  3. Elasticsearch安装(Linux)
  4. Activity之间的通信方式
  5. 2019年第十届蓝桥杯 - 省赛 - C/C++大学C组 - D. 质数
  6. linux水滴怎么抓包,tcpdump 抓包
  7. 反编译工具Reflector ILSpy
  8. flutter listview 滚动到底部_flutter实战项目,教你使用flutter打造仿微信app页面!...
  9. matplotlib——在 Jupyter Notebook中绘制图像时只显示变量信息不显示图片
  10. 白盒测试用例设计方法(语句覆盖、判定覆盖、条件覆盖)
  11. 微型计算机必须具备的输入设备,一台微型计算机必须具备的输出设备是显示器。...
  12. 【ERP软件】ERP体系二次开发有哪些危险?
  13. Python 中拼音库 PyPinyin 的使用
  14. java 新浪短链接_新浪t.cn短链接如何生成?网址缩短api接口分享
  15. 用Notepad++实现文本比较
  16. 基于javaweb框架的springboot mybatis宠物商城源码含论文设计文档
  17. 在windows终端中进行复制粘贴的方法
  18. python实现之一阶二阶导数
  19. 使用tortoisegit clone通过ssh clone远程库:invalid gitfile format: D:\Program Files\TortoiseGit\bin\Tortois“
  20. 基于javaweb+mysql的电影院售票购票电影票管理系统(前台、后台)

热门文章

  1. Havok物理引擎与Unity3D游戏的结合
  2. 北鼎新品K108小白壶全新亮相,经典搭配尽显百搭气场
  3. numpy中np.array()功能
  4. types.js?a742:39 Uncaught TypeError: Cannot read property ‘prototype‘ of undefined at eval (type
  5. 分布式系统原理(9)Paxos 协议
  6. 使用Psycopg2连接openGauss
  7. tomcat配置缓存溢出异常
  8. Rest ful风格同时传输一个对象和一个变量的几种方法
  9. 揭秘!在线称重设备是如何实现物品称重分选的
  10. 十、小程序实战 (IVX 快速开发教程)