想必大家肯定用过百度,当我们在百度的搜索框中输入文字的时候,下面就会自动出现你可能希望搜索的内容,但是页面中的其他内容都没有变化:
可以想象,这些内容都是根据在输入框中输入的内容动态变化的,而不是最开始就响应到浏览器中的,否则互联网中那么多内容,这个响应内容得有多大。我们也可以F12打开浏览器的调试模式,可以看到当你在输入框中每输入一个字符,就会有一个响应。
你可以想象如果现在没有Ajax技术,你会怎么做呢?我想,这也许是一个解决的办法:
之后每次响应的内容都重新和第一次响应的内容拼接起来,再次响应界面,从而刷新整个界面,但只有搜索框中的内容改变,而其他内容不变;但是这将导致一个问题,那就是重复的内容总是一遍又一遍的发送,这真的有必要吗?这也就是Ajax技术诞生的原因。
这里引用了百度百科的内容:
https://baike.baidu.com/item/ajax/8425?fr=aladdin
通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。传统的网页(不使用 Ajax)如果需要更新内容,必须重载整个网页页面。Ajax 在浏览器与 Web 服务器之间使用异步数据传输(HTTP 请求),这样就可使网页从服务器请求少量的信息,而不是整个页面。
使用 JavaScript 向服务器提出请求并处理响应而不阻塞用户核心对象XMLHttpRequest。通过这个对象,您的 JavaScript 可在不重载页面的情况与 Web 服务器交换数据,即在不需要刷新页面的情况下,就可以产生局部刷新的效果。
对于开发人员而言,知道以上这么多就可以了,相信有了上面的描述,你对Ajax技术已经有的一定的认识,下面我们将在实例中讲解如何使用Ajax技术。
下面给出一个演示的例子:
mainPage.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!doctype html>
<html lang="ch">
<head>
<base href="<%=basePath%>">
<title>mainPage</title>
<script type="text/javascript">
function getDate() {
//创建Ajax引擎对象
var ajax;
ajax = new XMLHttpRequest();
//复写onreadystatement函数
ajax.onreadystatechange = function () {
//获取响应内容,响应到方框内
var result = ajax.responseText;
var showdiv = document.getElementById("showdiv");
showdiv.innerHTML = result;
}
//发送请求
ajax.open("get", "ajaxtest");
ajax.send(null);
}
</script>
<style type="text/css">
#showdiv{
border: solid 1px;
width: 200px;
height: 100px;
}
</style>
</head>
<body>
<input type="button" value="test" onclick="getDate()">
<div id="showdiv"></div>
</body>
</html>
AjaxServlet.java
package com.chester.web.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/ajaxtest")
public class AjaxServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("good job!!");
}
}
启动服务器,在浏览器中访问mainPage.jsp文件,结果如下:
当我们点击test按钮,结果如下:
我们主要关注mainPage.jsp文件中JS脚本中的内容:
<script type="text/javascript">
function getDate() {
//创建Ajax引擎对象
var ajax;
ajax = new XMLHttpRequest();
//复写onreadystatement函数
ajax.onreadystatechange = function () {
//获取响应内容,响应到方框内
var result = ajax.responseText;
var showdiv = document.getElementById("showdiv");
showdiv.innerHTML = result;
}
//发送请求
ajax.open("get", "ajaxtest");
ajax.send(null);
}
</script>
可以看到使用ajax的步骤如下
创建引擎对象很简单,关于发送请求实际上和浏览器在地址栏发送是一个道理,这里open函数中写的参数是请求方式和请求地址。请求方式为get所以send的内容为null,如果请求方式为post,我们就需要在send中写入请求内容了(具体可以看下面的 五 Ajax的请求方式),下面着重讲解onreadystatechange函数。
onreadystatechange函数,从字面意思理解的话就是当readystate改变的时候;改变的时候,干什么呢?是执行。它实际上在上面只是声明重写了一下,我们没有直接调用这个函数,它是被调用的,声明一般而言我们都写的比较靠前,这也就是为什么函数的重写在发送请求之前。
关于readyState,它实际上是Ajax的一个状态,它有5个状态,每次改变的时候onreadystatechange函数就执行一次:
所以按下test按钮的时候,onreadystatechange应该执行了四次,那么我们知道只有当表示数据成功接收的时候我们才有执行的必要(对这里的上下文而言是这样的,你可以根据具体的情况做出改变,比如写一些测试代码),所以上面的JS脚本这样写更加合理:
<script type="text/javascript">
function getDate() {
//创建Ajax引擎对象
var ajax;
ajax = new XMLHttpRequest();
//复写onreadystatement函数
ajax.onreadystatechange = function () {
if(ajax.readyState == 4){
//获取响应内容,响应到方框内
var result = ajax.responseText;
var showdiv = document.getElementById("showdiv");
showdiv.innerHTML = result;
}
}
//发送请求
ajax.open("get", "ajaxtest");
ajax.send(null);
}
</script>
那么请求是否每次都会成功呢?不成功的话,难道我们要这样显示吗?
这显然是不合理的,至少我们应该给用户一些提示,所以我们的JS脚本又改成这样;
<script type="text/javascript">
function getDate() {
//创建Ajax引擎对象
var ajax;
ajax = new XMLHttpRequest();
//复写onreadystatement函数
ajax.onreadystatechange = function () {
//判断ajax状态
if(ajax.readyState == 4){
//判断响应状态码
if(ajax.status == 200){
//获取响应内容
var result = ajax.responseText;
var showdiv = document.getElementById("showdiv");
showdiv.innerHTML = result;
}
else if(ajax.status == 404){
var showdiv = document.getElementById("showdiv");
showdiv.innerText = "请求资源不存在"
}else if(ajax.status == 500){
var showdiv = document.getElementById("showdiv");
showdiv.innerText = "服务器繁忙"
}
}
}
//发送请求
ajax.open("get", "ajaxtest");
ajax.send(null);
}
</script>
其中ajax.status存储的是每次请求的状态码。
上面的内容就构成了一个基本的Ajax的应用。
我们应该意识到这样一个问题:当发送请求后,我们是阻塞在send函数处,等待响应的到来从而执行onreadystatechange函数,还是执行完send函数之后,直接执行函数中接下来的内容,当响应到来再执行onreadystatechange函数;等待就是同步,直接执行下面的内容就是异步。
默认我们是使用异步的方式,在我们的open函数中,有这样一个参数选项,比如我们改成同步的方式:
ajax.open("get", "ajaxtest", false);
同步的方式在实际使用中还是比较少的。
在上面的情况中,如果我们点击test按钮,而服务器未及时响应(比如在AjaxServlet.java中写Thread.sleep(3000);),我们希望可以给用户一些提示,而不是保持原有的空白框,这很简单,我们只需要这样修改js代码:
<script type="text/javascript">
function getDate() {
//创建Ajax引擎对象
var ajax;
ajax = new XMLHttpRequest();
//复写onreadystatement函数
ajax.onreadystatechange = function () {
//判断ajax状态
if(ajax.readyState == 4){
//判断响应状态码
if(ajax.status == 200){
//获取响应内容
var result = ajax.responseText;
var showdiv = document.getElementById("showdiv");
showdiv.innerHTML = result;
}
else if(ajax.status == 404){
var showdiv = document.getElementById("showdiv");
showdiv.innerText = "请求资源不存在"
}else if(ajax.status == 500){
var showdiv = document.getElementById("showdiv");
showdiv.innerText = "服务器繁忙"
}
}else{
var showdiv = document.getElementById("showdiv");
showdiv.innerHTML=<img src='img/waiting.gif' width='200px' height='100px'/>;
}
}
//发送请求
ajax.open("get", "ajaxtest");
ajax.send(null);
}
</script>
在else选项中显示waiting.gif图片即可。
在使用Ajax技术的时候,我们要意识到ajax发送请求的时候,实际上请求还是浏览器发出的,只是浏览器知道这个请求是ajax发出的,当响应到来的时候,就不刷新整个界面了,而是响应给ajax;ajax再做其他的操作。所以对服务器来说,是没有ajax方式还是普通方式请求的区别的。
前面的例子,我们只是使用了简单的get方式,当我们需要使用get来提交数据的时候,我们可以这样写
ajax.open("get", "ajaxtest?name=chester&pwd=123");
ajax.send(null);
作为一个练习,你可以想象一下,如果我们要做一个用户注册功能,ajax部分的代码应该如何写。自然就是获取用户输入,而后拼接到请求路径中:
ajax.open("get", "ajaxtest?name=" + name + "&pwd=" + pwd);
其中name 和 pwd这样得到:
var name = document.getElementById("uname").value;
var pwd = document.getElementById("pwd").value;
如果使用post方式提交数据,我们可以这样写
ajax.open("get", "ajaxtest");
ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
ajax.send(name=chester&pwd=123);
注意这里加了一句:
ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
这句话的意思是,告诉服务器按照键值对的方式解析post方式send的数据,否则服务器会认为只是普通的字符串提交。
以上就是Ajax技术的主要内容了,我们在来讲讲一个非常常见的问题,即数据交互问题,对于普通的简单内容,可能直接一个普通字符串就可以了,就像上面的例子一样,但是对于类似表格这样的很多数内容应该如何处理呢?
有一点是毋庸置疑的,那就是我们在服务器端必须要保证,响应的格式要能被JS所解析,一种是普通字符串,另一种是JSON(重点)和XML;其实JSON也是一种字符串,但是用它来表示长长的数据(比如表格中的各项数据)比普通字符串要好得多,而且也很方便。
为了演示JSON的使用,我们编写如下代码:
mainPage.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<!doctype html>
<html lang="ch">
<head>
<title>mainPage</title>
<script type="text/javascript">
function getTableDate() {
//获取用户请求信息
var name = document.getElementById("uname").value;
var pwd = document.getElementById("pwd").value;
//创建Ajax引擎对象
var ajax;
ajax = new XMLHttpRequest();
//复写onreadystatement函数
ajax.onreadystatechange = function(){
//判断ajax状态
if(ajax.readyState == 4){
//判断响应状态码
if(ajax.status == 200){
//获取响应内容
var result = ajax.responseText;
eval("var user=" + result);
//获取表格对象
var ta = document.getElementById("ta");
//插入新行
var tr = ta.insertRow(1);
//在新行插入数据
var cell0 = tr.insertCell(0);
cell0.innerHTML=user.uid;
var cell1 = tr.insertCell(1);
cell1.innerHTML=user.uname;
var cell2 = tr.insertCell(2);
cell2.innerHTML=user.pwd;
}else if(ajax.status == 404){
var showdiv = document.getElementById("showdiv");
showdiv.innerText = "请求资源不存在"
}else if(ajax.status == 500){
var showdiv = document.getElementById("showdiv");
showdiv.innerText = "服务器繁忙"
}
}
}
//发送请求
ajax.open("get", "ajaxtest?name=" + name + "&pwd=" + pwd);
ajax.send(null);
}
</script>
</head>
<body>
<hr>
请输入需要搜索的用户名和密码:<input type="text" name="uname" placeholder="Username" id="uname"/>
<input type="text" name="pwd" placeholder="Userpassword" id="pwd"/>
<input type="button" value="search" onclick="getTableDate()">
<hr>
<table border="1px" id="ta">
<tr>
<td>id</td>
<td>name</td>
<td>pwd</td>
</tr>
</table>
</body>
</html>
AjaxServlet.java
package com.chester.web.servlet;
import com.chester.web.pojo.User;
import com.chester.web.service.LoginService;
import com.chester.web.service.impl.LoginServiceImpl;
import com.google.gson.Gson;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/ajaxtest")
public class AjaxServlet extends HttpServlet{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
String name = req.getParameter("name");
String pwd = req.getParameter("pwd");
System.out.println("用户提交信息为" + name + pwd);
LoginService ls = new LoginServiceImpl();
User u = ls.checkLoginService(name, pwd);
if(u != null){
System.out.println("查询到的用户信息" + u);
resp.getWriter().write(new Gson().toJson(u));
}
}
}
在代码中,我们实现这样一个功能,新建一个表格,提示用户输入用户名和密码,而后到后台查询信息,响应到表格中,效果如下所示:
输入用户名和密码,点击search后,显示如下:
这里主要讲解如下几个代码段,其他的都比较容易理解:
//获取响应内容
var result = ajax.responseText;
eval("var user=" + result);
这里从服务器的响应获取JSON格式的数据,而后将其赋给user变量,eval()函数是JS的一个函数,它可以计算某个字符串,并执行其中的的 JavaScript 代码。为了知道result中的内容我们在其中插入一句:
//获取响应内容
var result = ajax.responseText;
alert(result);
eval("var user=" + result);
再次查看响应,可以看到浏览器的提示框中输出如下信息:
这是JSON的字符串。我们在表格中并没用全部显示这些信息,而是选择了一部分显示。
关于服务器端JSON数据的创建比较简单,我们使用了Google公司开发的jar包gson,直接在servlet中调用,当然你也可以自己拼接出这样的字符串。
resp.getWriter().write(new Gson().toJson(u));
使用Gson()类的toJson()方法将User中的数据域转化成为JSON数据,非常的方便,其中User对象是这样的:
public class User {
private int uid;
private String uname;
private String pwd;
private int sex;
private int age;
private String birth;
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getBirth() {
return birth;
}
public void setBirth(String birth) {
this.birth = birth;
}
public String toString(){
return getUname() + getPwd() + getBirth();
}
}
如果输入的是List对象,转化的结果就是一个数组,数组的每一项就是List对象中的每一项数据对应的JSON数据,看起来类似于:
[{"uid":1,"uname":"Florence West","pwd":"123","sex":1,"age":18,"birth":"2000-10-10"},
{"uid":1,"uname":"Florence West","pwd":"123","sex":1,"age":18,"birth":"2000-10-10"},
{"uid":1,"uname":"Florence West","pwd":"123","sex":1,"age":18,"birth":"2000-10-10"}]
这样一来,我们就成功的将java中的数据对象传递给了JS对象。
关于使用XML传递数据,和JSON实际上大同小异:
数据发送端(java servlet):
//将响应数据类型告诉浏览器为XML格式
resp.setContentType("text/xml;charset=utf-8");
//发送数据给浏览器
resp.getWriter().write("1 chester ");
我们也可以使用jsp文件来实现这段代码
数据接收端(js):
//获取
var result = ajax.responseXML;
//使用
alert(result.getElementsByTagName("uname")[0].innerHTML)
可以看到XML比JSON要麻烦很多,而且,如果User对象改变,XML内容也需要更改,而JSON不用。所以如果我们需要数据传输,尽量使用JSON,XML一般是用来做配置文件。
经过上面的描述我们可以看到,如果在其他地方需要再次使用Ajax,有很多内容是重复的,这也引导我们也许可以将Ajax封装成一个JS函数,这个任务并不难,可以自己尝试一下。