Ajax的定义
ajax是多种技术的统称,它向服务器发送请求的一种手段,但发送请求时不会导致页面刷新或跳转。
a - asynchronous - 可以通过 xhr 对象发送异步请求
j - javascript
a - and
x - xml - 指响应格式 ,后来应用主流是json,但是名字已经定下,这里的x'也可以说是xml和json
XMLHttpRequest 对象的基本使用
- 创建 xhr 对象
var xhr = new XMLHttpRequest();
- 发送请求
xhr.open("get|post", 请求地址, true|false); // 发请求前准备
xhr.send(); // 真正发送请求open的第一个参数
post 请求
1、get 请求只有请求行和请求头
2、post 请求有行、请求头、请求体,下面是post发送请求实例var xhr = new XMLHttpRequest();
xhr.onload = function(){};// 发请求
xhr.open("post", url, true);
xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded"); // 告诉服务器,请求体的格式是:表单格式
xhr.send("请求体内容"); // 地址?参数名=参数值&参数名=参数值...open的第三个参数
true 默认值 -- 异步请求, send 方法不会阻塞,页面其他代码,视频都不会等待响应,继续执行
false -- 同步请求 响应没有返回之前,页面代码、视频都会暂停,直到响应返回为止,send方法在此期间一直处于阻塞状态异步请求下需要使用事件的机制来接收响应
// (新)响应返回会触发 onload事件,执行事件对应的函数
xhr.onload = function () {};
// (旧)发送请求时会触发, 响应返回时会触发, 响应完全返回时会触发
xhr.onreadystatechange = function() {
//xhr.readyState // 状态
//以4秒触发为例, xhr 创建时 0
// xhr.send 0-->1
// xhr. 1--> 2 2-->3
// 3--> 4 表示响应完全返回
if(xhr.readyState == 4) {}
}
- 接收响应
xhr.responseText 需要的响应通常不是一个完整的html, 而是一个html片段,或是一个字符串
代码演示
模拟弹幕,发文字 ,视频不会暂停(也就是不会刷新视频)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script>
function sendRequest() {
// 1.创建 xhr 对象
var xhr = new XMLHttpRequest();
// 2. 定义 onload 事件
xhr.onload = function() {
// 5. 处理服务器端的响应
document.getElementById("result").innerText = xhr.responseText;
};
//得到id="c" 这个文本框的值
var v = document.getElementById("c").value;
// 3. 发送请求,把文本框的值当做参数传给页面,以便页面可以拿到该值,并显示出来
xhr.open("get", "/commentServlet?comment=" + v, true);
xhr.send();
}
//以上的全部可以写成以下
/*function sendRequest() {
// url 请求地址 例如:/commentServlet
// param 请求参数 例如: "comment=" + document.getElementById("c").value
// callback 回调函数 在响应返回时被调用
post("/commentServlet", "comment=" + document.getElementById("c").value, function (x) {
document.getElementById("result").innerText = x.responseText;
});
}*/
</script>
</head>
<body>
<video src="1.mp4" controls width="500"></video>
<hr>
请输入评论:<input type="text" name="comment" id="c">
<<%--input type="submit" value="提交评论">--%>
<input type="button" value="用xhr发送请求" onclick="sendRequest()">
<hr>
<span id="result"></span>
</body>
</html>
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/commentServlet")
public class CommentServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 4.
req.setCharacterEncoding("utf-8");
//在服务器输出输入的文字内容
String comment = req.getParameter("comment");
System.out.println("comment:" + comment);
// 返回响应
resp.setCharacterEncoding("utf-8");
resp.getWriter().print(comment); // 在浏览器上输出输入的内容
}
}
结果
响应格式
主流格式有两种:
- xml (可扩展标记语言)
<response>
<time>2019-1-19 11:43:33</time>
<author>张三</author>
<content>还不错</content>
<score>11</score>
</response>
var xml = xhr.responseXML;
xml.getElementsByTagName("time")[0].innerText;//time表示response里的所有标签,拿到0位置的数据,也就是time标签里的值
- json (javascript object 对象 notation 标记)
{
"time":"2019-1-19 11:43:33",
"author":"张三",
"content": "还不错",
"score": 11
}
var obj = xhr.responseXML;
obj.time //表示拿到time的值json中除了解析方便外,还支持更多类型 字符串、数字、布尔值、数组、对象
json(jackson)
JSON 是存储和交换文本信息的语法,类似 XML,但JSON 比 XML 更小、更快,更易解析。
1. 把 java 对象转换为 json 字符串, 或反之则转换为java,转换json的第三方 api, gson(谷歌),jackson(spring),fastjson(阿里)
2. js 对象和 json 字符串相互转换
var js对象 = JSON.parse(json字符串);
var json字符串 = JSON.stringify(js对象);3、如果要对日期进行控制,在日期属性上添加注解: @JsonFormat(pattern="日期格式", timezone="GMT+08")
timezone表示时区,GMT加上8刚好是中国时间
4、如果要忽略某个属性:在属性上添加一个 @JsonIgnore 注解
5、如果要转换后改变属性名:@JsonProperty("新属性名")
json重要的类
ObjectMapper om = new ObjectMapper();
String json = om.writeValueAsString(java对象);//把java对象转变为json
还原操作
ObjectMapper om = new ObjectMapper();
类型 java对象 = om.readValue(json字符串, 类.class);//把json转变为java对象
代码演示
1、json操作集合,无需遍历,代码简洁
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Date;
public class Student {
public Student(int id, String password, String name, String sex, Date birthday) {
this.id = id;
this.password = password;
this.name = name;
this.sex = sex;
this.birthday = birthday;
}
public Student() {
}
private int id;
//用该注解可以忽略大小写
@JsonIgnore
private String password;
//用该注解可以把声明的属性,序列化为括号里的名字,也就是序列化
@JsonProperty("username")
private String name;
private String sex;
//用该注解可以指定日期格式,
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08")
private Date birthday;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", password='" + password + '\'' +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", birthday=" + birthday +
'}';
}
}
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@WebServlet("/students")
public class StudentServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//指定响应的编码方式
resp.setCharacterEncoding("utf-8");
//指定响应的内容类型
resp.setContentType("application/json;charset=utf-8");
List<Student> list = new ArrayList<>();
list.add(new Student(1, "123", "张三", "男", new Date()));
list.add(new Student(2, "456", "李四", "男", new Date()));
list.add(new Student(3, "456", "王五", "男", new Date()));
list.add(new Student(4, "678", "赵六", "男", new Date()));
ObjectMapper om = new ObjectMapper();
//以下是浏览器输出的两种方式,这样方便了遍历
// 方法1:
// String json = om.writeValueAsString(list);
// resp.getWriter().print(json);
// 方法2:
om.writerWithDefaultPrettyPrinter().writeValue(resp.getWriter(), list);
}
}
结果
这里的集合是用【】括起来的,里面包含花括号的集合内容
2、将json字符串转换为js字符串,并输出到console上
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script>
function getJsonData(){
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var json = xhr.responseText;
// 使用 JSON.parse(json字符串) ==> 转换为 js 的对象或数组
var array = JSON.parse(json);
for(var i = 0; i < array.length; i++) {
//输出系列化注解的那个username
console.log(array[i].username);
}
};
xhr.open("get", "/students", true);
xhr.send();
}
</script>
</head>
<body>
<input type="button" value="获取json结果" onclick="getJsonData()">
</body>
</html>
结果
servlet 中结合 jackson
1、自动补全
存储信息的代码
public class Region {
private int regionId;
private int regionCode;
private String regionName;
private int parentId;
private String regionNameEn;
public int getRegionId() {
return regionId;
}
public void setRegionId(int regionId) {
this.regionId = regionId;
}
public int getRegionCode() {
return regionCode;
}
public void setRegionCode(int regionCode) {
this.regionCode = regionCode;
}
public String getRegionName() {
return regionName;
}
public void setRegionName(String regionName) {
this.regionName = regionName;
}
public int getParentId() {
return parentId;
}
public void setParentId(int parentId) {
this.parentId = parentId;
}
public String getRegionNameEn() {
return regionNameEn;
}
public void setRegionNameEn(String regionNameEn) {
this.regionNameEn = regionNameEn;
}
}
连接数据库
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* jdbc工具类
* @author yihang
*/
public class JdbcUtil {
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.out.println("加载驱动失败");
}
}
/**
* 获取数据库连接
* @return 连接对象
*/
public static Connection getConnection() {
try {
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/rbac", "root", "root");
return conn;
} catch (SQLException e) {
System.out.println("获取连接失败" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 关闭资源(包括ResultSet, Statement等)
* @param resources 资源对象
*/
public static void close(AutoCloseable... resources) {
for (AutoCloseable resource : resources) {
if(resource !=null) {
try {
resource.close();
} catch (Exception e) {
System.out.println("关闭资源失败" + e.getMessage());
throw new RuntimeException(e);
}
}
}
}
}
搜索信息的代码
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class RegionDao {
// 模糊查询地名
public List<Region> findByName(String name) {
String sql = "select * from region where region_name like ?";
try(Connection conn = JdbcUtil.getConnection()) {
try(PreparedStatement stmt = conn.prepareStatement(sql)){
// name = 西
stmt.setString(1, name + "%");
ResultSet rs = stmt.executeQuery();
List<Region> list = new ArrayList<>();
while(rs.next()) {
Region region = new Region();
region.setRegionId(rs.getInt("region_id"));
region.setRegionCode(rs.getInt("region_code"));
region.setRegionName(rs.getString("region_name"));
region.setParentId(rs.getInt("parent_id"));
region.setRegionNameEn(rs.getString("region_name_en"));
list.add(region);
}
return list;
}
} catch (SQLException e) {
e.printStackTrace();
return Collections.emptyList();
}
}
}
jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<style>
li {
list-style: none;
}
li:hover {
background: blue;
color:white;
}
ul {
display: none;
border: 1px solid rgb(238,238,238);
margin: 0;
padding: 0;
width: 160px;
box-shadow: 0px 1px 6px rgba(0,0,0,0.2);
border-radius: 4px ;
font-size: 13px;
}
</style>
</head>
<body>
<%--根据输入的第一个字扩张出有关地名,失去焦点时隐藏菜单,单击扩展出来的地名,显示在文本框里,并且菜单隐藏,再点击文本框时,菜单有显示出来--%>
<input type="text" id="name" onkeyup="findByName()" onblur="hideResult()" onclick="toggleResult()">
<ul id="result"></ul>
<script>
//触发该函数单击时如果没有菜单则显示,否者则不显示
function toggleResult() {
if(document.getElementById("result").style.display == "none") {
document.getElementById("result").style.display = "block";
} else {
document.getElementById("result").style.display = "none";
}
}
//触发该函数隐藏菜单
function hideResult() { // 14:19:00
setTimeout(function(){
document.getElementById("result").style.display = "none"; // 14:19:00:003
}, 300);
}
//触发该函数找到扩展地名
function findByName() {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var json = xhr.responseText;
var list = JSON.parse(json);
document.getElementById("result").innerHTML="";
for(let i = 0; i < list.length; i++) {
let li = document.createElement("li");
li.innerText = list[i].regionName;
// li 随着列表隐藏了起来
li.onclick = function(){
document.getElementById("name").value = li.innerText;
document.getElementById("result").style.display = "none";
};
document.getElementById("result").appendChild(li);
}
document.getElementById("result").style.display = "block";
};
var v = document.getElementById("name").value;
if( v != "" ) {
xhr.open("get", "http://localhost:8080/regionByName?name=" + v);
xhr.send();
}
}
</script>
</body>
</html>
servlet代码
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@WebServlet(urlPatterns = "/regionByName")
public class RegionNameServlet extends HttpServlet {
private RegionDao regionDao = new RegionDao();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获得参数name的值,也就是想要模糊查询的字
String name = req.getParameter("name");
//把上面拿到的字进行模糊查询
List<Region> list = regionDao.findByName(name);
//指定响应编码方式和内容类型
resp.setCharacterEncoding("utf-8");
resp.setContentType("application/json;charset=utf-8");
ObjectMapper om = new ObjectMapper();
//把值的集合输出到浏览器上
om.writeValue(resp.getWriter(), list);
}
}
结果
2、省级联动(根据选出的省,市只能选省包含的市,县只能选市包含的县)
存储地名的信息的代码
public class Region {
private int regionId;
private int regionCode;
private String regionName;
private int parentId;
private String regionNameEn;
public int getRegionId() {
return regionId;
}
public void setRegionId(int regionId) {
this.regionId = regionId;
}
public int getRegionCode() {
return regionCode;
}
public void setRegionCode(int regionCode) {
this.regionCode = regionCode;
}
public String getRegionName() {
return regionName;
}
public void setRegionName(String regionName) {
this.regionName = regionName;
}
public int getParentId() {
return parentId;
}
public void setParentId(int parentId) {
this.parentId = parentId;
}
public String getRegionNameEn() {
return regionNameEn;
}
public void setRegionNameEn(String regionNameEn) {
this.regionNameEn = regionNameEn;
}
}
连接数据库
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* jdbc工具类
* @author yihang
*/
public class JdbcUtil {
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.out.println("加载驱动失败");
}
}
/**
* 获取数据库连接
* @return 连接对象
*/
public static Connection getConnection() {
try {
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/rbac", "root", "root");
return conn;
} catch (SQLException e) {
System.out.println("获取连接失败" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 关闭资源(包括ResultSet, Statement等)
* @param resources 资源对象
*/
public static void close(AutoCloseable... resources) {
for (AutoCloseable resource : resources) {
if(resource !=null) {
try {
resource.close();
} catch (Exception e) {
System.out.println("关闭资源失败" + e.getMessage());
throw new RuntimeException(e);
}
}
}
}
}
根据查询id查询地名
import domain.Region;
import util.JdbcUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class RegionDao {
public List<Region> findByParentId(int parentId) {
String sql = "select * from region where parent_id = ?";
try (Connection conn = JdbcUtil.getConnection()) {
try(PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, parentId);
ResultSet rs = stmt.executeQuery();
List<Region> list = new ArrayList<>();
while(rs.next()) {
Region region = new Region();
region.setRegionId(rs.getInt("region_id"));
region.setRegionCode(rs.getInt("region_code"));
region.setRegionName(rs.getString("region_name"));
region.setParentId(rs.getInt("parent_id"));
region.setRegionNameEn(rs.getString("region_name_en"));
list.add(region);
}
return list;
}
} catch (SQLException e) {
e.printStackTrace();
return Collections.emptyList();
}
}
}
jsp页面及联动代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
省 <select id="sheng" onchange="findShi()"></select>
市 <select id="shi" onchange="findxian()"></select>
县 <select id="xian"></select>
<script>
//console.log(document.getElementById("sheng"));
function findxian() {
// 获取省当前选中的 option 的 value
var parentId = document.getElementById("shi").value;
var xhr = new XMLHttpRequest();
xhr.onload = function () {
var json = xhr.responseText;
var list = JSON.parse(json);
// 添加之前要清除下拉列表的内容
document.getElementById("xian").innerHTML = "";
// 添加新内容
for(var i = 0 ; i < list.length; i++) {
var region = list[i];
// js 生成标签
var option = document.createElement("option"); // 创建<option></option>标签
option.value = region.regionId; // 给option的value赋值
option.innerText = region.regionName; // 给option的赋予内容
// 把它添加给 市 这个父元素
document.getElementById("xian").appendChild(option);
}
};
xhr.open("get", "/region?parentId=" + parentId, true);
xhr.send();
}
function findShi () {
// 获取省当前选中的 option 的 value
var parentId = document.getElementById("sheng").value;
var xhr = new XMLHttpRequest();
xhr.onload = function () {
var json = xhr.responseText;
var list = JSON.parse(json);
// 添加之前要清除下拉列表的内容
document.getElementById("shi").innerHTML = "";
// 添加新内容
for(var i = 0 ; i < list.length; i++) {
var region = list[i];
// js 生成标签
var option = document.createElement("option"); // 创建<option></option>标签
option.value = region.regionId; // 给option的value赋值
option.innerText = region.regionName; // 给option的赋予内容
// 把它添加给 市 这个父元素
document.getElementById("shi").appendChild(option);
}
// 联动
findxian ();
};
xhr.open("get", "/region?parentId=" + parentId, true);
xhr.send();
}
function findSheng() {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var json = xhr.responseText;
var list = JSON.parse(json);
for(var i = 0 ; i < list.length; i++) {
var region = list[i];
// js 生成标签
var option = document.createElement("option"); // 创建<option></option>标签
option.value = region.regionId; // 给option的value赋值
option.innerText = region.regionName; // 给option的赋予内容
// 把它添加给 省 这个父元素
document.getElementById("sheng").appendChild(option);
}
// 联动
findShi ();
};
xhr.open("get", "/region?parentId=1", true);
xhr.send();
}
findSheng();
</script>
</body>
</html>
servlet代码
@WebServlet("/region")
public class RegionServlet extends HttpServlet {
private RegionDao regionDao = new RegionDao();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//得到发送过来的参数parentId的值
String parentId = req.getParameter("parentId");
//根据parentId查找子地名集合
List<Region> list = regionDao.findByParentId(Integer.valueOf(parentId));
resp.setCharacterEncoding("utf-8");
resp.setContentType("application/json;charset=utf-8");
ObjectMapper om = new ObjectMapper();
//输出到浏览器上
om.writeValue(resp.getWriter(), list);
}
}
结果
总结
ajax可以做评论弹幕、图形验证码的验证、级联动(省级联动)等,这样不需要刷新便可以显示信息,更加人性化,体验感更好,因此做有关页面的项目时,不要忘记它,还有就是发送时有同步和异步两个模式,异步请求就是请求发送后,不必等待响应返回,接下来的代码可以继续执行,而同步需要等待结束,才能继续执行,把open里的true就是异步,false就同步,一般都是些异步。