Javascript 基础---Ajax入门必看


Posted in Javascript onJuly 06, 2016

Ajax 是现代Web 应用程序开发的一项关键工具。它让你能向服务器异步发送和接收数据,然后用 Javascript 解析。 Ajax 是 Asynchronous JavaScript and XML (异步JavaScript 与XML)的缩写。

Ajax 核心规范的名称继承于用来建立和发起请求的 Javascript 对象:XMLHttpRequest 。这个规范有两个等级。所有主流浏览器都实现了第一级,它代表了基础级别的功能。第二级扩展了最初的规范,纳入了额外的事件和一些功能来让它更容易与 form 元素协作,并且支持一些相关规范。 

1. Ajax起步

Ajax 的关键在于 XMLHttpRequest 对象,而理解这个对象的方法是看个例子。下面代码展示了 XMLHttpRequest 对象的简单用法:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example</title>
</head>
<body>
<div>
  <button>apples</button>
  <button>cherries</button>
  <button>bananas</button>
</div>
<div id="target">
  Press a button
</div>

<script type="application/javascript">
 var buttons = document.getElementsByTagName("button");
  for(var i=0; i<buttons.length; i++){
    buttons[i].onclick = handleButtonPress;
  }

  //脚本会调用此函数以响应 button 控件的 click 事件
  function handleButtonPress(e){
    //创建一个新的 XMLHttpRequest 对象
    var httpRequest = new XMLHttpRequest();
    //给 onreadystatechange 事件设置一个事件处理器
    httpRequest.onreadystatechange = handleResponse;
    //使用 open 方法来指定 HTTP 方法和需要请求的 URL (即告诉 httpRequest 对象你想要做的事)
    httpRequest.open("GET", e.target.innerHTML+".html");
    //这里没有向服务器发送任何数据,所以 send 方法无参数可用
    httpRequest.send();
  }

  //处理响应
  //一旦脚本调用了 send 方法,浏览器就会在后台发送请求到服务器。因为请求是在后台处理的,所以Ajax 依靠事件来通知这个请求的进展情况。
  function handleResponse(e){
    //当 onreadystatechange 事件被触发后,浏览器会把一个 Event 对象传递给指定的处理函数,target 属性则会被设为与此事件关联的XMLHttpRequest
    if(e.target.readyState == XMLHttpRequest.DONE && e.target.status == 200){ //请求成功
      document.getElementById("target").innerHTML = e.target.responseText; //显示被请求文档的内容
    }
  }
</script>
</body>
</html>

三个额外的文档非常简单:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Apples</title>
  <style>
    img { float:left;padding:2px;margin:5px;border: medium double black;background-color: lightgrey; width: 100px;height: 100px;}
  </style>
</head>
<body>
<p>
  <img src="../img/show-page/img_apples.jpg"/>
  Page for apples.
</p>
</body>
</html>

效果如下图所示:

Javascript 基础---Ajax入门必看

随着用户点击各个水果按钮,浏览器会异步执行并取回所请求的文档,而主文档不会被重新加载。这就是典型的 Ajax 行为。

 2. 使用 Ajax 事件

建立和探索一个简单的示例之后,可以开始深入了解 XMLHttpRequest 对象支持的功能,以及如何在请求中使用它们了。起点就是第二级规范里定义的那些额外事件:

Javascript 基础---Ajax入门必看

这些事件大多数会在请求的某一特定时间点上触发。 readystatechange 和 progress 这两个事件是例外,它们可以多次触发以提供进度更新。

调度这些事件时,浏览器会对 readystatechange 事件使用常规的 Event 对象,对其他事件则使用 ProgressEvent 对象。 ProgressEvent 对象定义了 Event 对象的所有成员,并增加了下图中介绍的这些成员:

Javascript 基础---Ajax入门必看

下面代码展示了如何使用这些事件:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example</title>
  <style>
    table {margin: 10px;border-collapse: collapse; float: left;}
    div{margin: 10px;}
    td,th{padding: 4px;}
  </style>
</head>
<body>
<div>
  <button>apples</button>
  <button>cherries</button>
  <button>bananas</button>
</div>
<table id="events" border="1"></table>
<div id="target">
  Press a button
</div>

<script type="application/javascript">
  var buttons = document.getElementsByTagName("button");
  for(var i=0; i<buttons.length; i++){
    buttons[i].onclick = handleButtonPress;
  }

    var httpRequest;
  function handleButtonPress(e){
    clearEventDetails();
    httpRequest = new XMLHttpRequest();
    httpRequest.onreadystatechange = handleResponse;

    httpRequest.onerror = handleError;
    httpRequest.onload = handleLoad;;
    httpRequest.onloadend = handleLoadEnd;
    httpRequest.onloadstart = handleLoadStart;;
    httpRequest.onprogress = handleProgress;

    httpRequest.open("GET", e.target.innerHTML+".html");
    httpRequest.send();
  }

  function handleResponse(e){
    displayEventDetails("readystate("+httpRequest.readyState+")")
    if(e.target.readyState == XMLHttpRequest.DONE && e.target.status == 200){
      document.getElementById("target").innerHTML = e.target.responseText;
    }
  }

  function handleError(e){ displayEventDetails("error",e);}
  function handleLoad(e){ displayEventDetails("load",e);}
  function handleLoadEnd(e){ displayEventDetails("loadend",e);}
  function handleLoadStart(e){ displayEventDetails("loadstart",e);}
  function handleProgress(e){ displayEventDetails("progress",e);}

  function clearEventDetails(){
    document.getElementById("events").innerHTML = "<tr><th>Event</th><th>lengthComputable</th><th>loaded</th><th>total</th>";
  }

  function displayEventDetails(eventName,e){
    if(e){
      document.getElementById("events").innerHTML
          +="<tr><td>"+eventName+"</td><td>"+ e.lengthComputable+"</td><td>"+ e.loaded+"</td><td>"+ e.total+"</td></tr>";
    }else {
      document.getElementById("events").innerHTML += "<tr><td>"+eventName+"</td><td>NA</td><td>NA</td><td>NA</td></tr>";
    }
  }
</script>
</body>
</html>

这是之前示例的一种变型,为一些事件注册了处理函数,并在一个 table 元素里为处理的每个事件都创建了一条记录。从下图中可以看到 Firefox 浏览器是如何触发这些事件的。

Javascript 基础---Ajax入门必看

3. 处理错误

使用 Ajax 时必须留心两类错误。它们之间的区别源于视角不同。

第一类错误是从 XMLHttpRequest 对象的角度看到的问题:某些因素阻止了请求发送到服务器。例如 DNS 无法解析主机名,连接请求被拒绝,或者URL无效。

第二类问题是从应用程序的角度看到的问题,而非 XMLHttpRequest 对象。它们发生于请求成功发送至服务器,服务器接收请求、进行处理并生成响应,但该响应并不指向你期望的内容。例如,如果请求的URL 不存在,这类问题就会发生。

有三种方式可以处理这些错误,如下面代码所示:

3.1 处理设置错误

需要处理的第一类问题是向 XMLHttpResquest 对象传递了错误的数据,比如格式不正确的 URL 。它们极其容易发生在生成基于用户输入的URL 时。为了模拟这类问题,上面文档中有添加一个标签 Bad URL (错误的URL)的button 。按下这个按钮会以以下形式调用 open 方法:

httpRequest.open("GET","http://")

 这是一种会阻止请求执行的错误,而 XMLHttpRequest 对象会发生这类事件时抛出一个错误。这就意味着需要用一条 try...catch 语句来围住设置请求的代码,就像这样:

 

try{
      ...
      httpRequest.open("GET","http://")
      ...
      httpRequest.send();
    }catch(error){
      displayErrorMsg("try/catch",error.message)
    }

 catch 子句让你有机会从错误中恢复。可以选择提示用户输入一个值,也可以回退至默认的URL ,或是简单地丢弃这个请求。 在这个例子中,仅仅调用了 displayErrorMsg 函数来显示错误消息。

3.2 处理请求错误

第二类错误发生在请求已生成,但其他方面出错时。为了模拟这类问题,在示例中添加了一个标签为 Bad Host (错误主机)的按钮。当这个按钮被按下后,就会调用 open 方法访问一个不可用的 URL:

httpRequest.open("GET",http://www.ycdoitt.com/nopage.html)

这个URL 存在两个问题。第一个问题是主机名不能被 DNS 解析,因此浏览器无法生成服务器连接。这个问题知道 XMLHttpRequest 对象开始生成请求时才会变得明显,因此它会以两种方式发出错误信号。如果你注册了一个 error 事件的监听器,浏览器就会向你的监听函数发送一个 Event 对象。以下是示例中使用的函数:

function handleError(e){
    displayErrorMsg("Error event",httpRequest.status + httpRequest.statusText);
  }

 当这类错误发生时,能从 XMLHttpRequest 对象获得何种程度的信息取决于浏览器,遗憾的是大多数情况下,会得到的值为 0的 status和空白的 statusText 值。

第二个问题是URL和生成请求的具有不同的来源,在默认情况下这是不允许的。你通常只能向载入脚本的同源URL发送Ajax请求。浏览器报告这个问题时可能会抛出 Error 或者触发error事件,不同浏览器的处理方法不尽相同。不同浏览器还会在不同的时点检查来源,这就意味着不一定总是能看到浏览器对同一问题突出显示。可以使用跨站资源规范(CORS,Cross-Origin Resource Sharing)来绕过同源限制。

3.3 处理应用程序错误

最后一类错误发生于请求成功完成(从XMLHttpRequest对象的角度看),但没有返回你想要的数据时。为了制造这类问题,在上面示例中添加一个说明标签为 cucumber 的 button 。按下这个按钮会生成类似于 apples、cherries 和 bananas 按钮那样的请求URL,但是在服务器上不存在 cucumber.html 这个文档。

这一过程本身没有错误(因为请求已完成),需要根据 status属性来确定发生了什么。当请求某个存在的文档时,会获得404这个状态码,它的意思是服务器无法找到请求的文档。可以看到示例是如何处理200(意思是OK)以外的状态码的:

if(httpRequest.status == 200){
        target.innerHTML = httpRequest.responseText;
      }else{
        document.getElementById("statusmsg").innerHTML
            = "Status:" + httpRequest.status +" "+ httpRequest.statusText;
      }

在这个例子中,只是简单的显示了status和statusText的值。而在真正的应用程序里,需要以一种有用且有意义的方式进行恢复(比如显示备用内容或警告用户有问题,具体看哪种更适合应用程序)。

 4. 获取和设置标头

使用XMLHttpRequest对象,可以设置发送给服务器的请求标头(Header)和读取服务器响应里的标头。

Javascript 基础---Ajax入门必看

 

4.1 覆盖请求的HTTP方法

通常不需要添加或修改Ajax请求里的标头。浏览器知道需要发送些什么,服务器也知道如何进行响应。不过,有几种情况例外。第一种是 X-HTTP-Method-Override 标头。

HTTP标准通常被用于在互联网上请求和传输HTML文档,它定义了许多方法。大多数人都知道GET和POST,因为它们的使用最为广泛。不过还存在其他一些方法(包括PUT和DELETE),这些HTTP方法用来给向服务器请求的URL赋予意义,而且这种用法正在呈现上升趋势。举个例子,假如想查看某条用户记录,可以生成这样一个请求:

httpRequest.open("GET","http://myserver/records/freeman/adam");

 这里只展示了HTTP方法和请求的URL。要使这个请求能顺利工作,服务器端必须由应用程序能理解这个请求,并将它转变成一段合适的数据以发送回服务器。如果想删除数据,可以这么写:

httpRequest.open("DELETE","http://myserver/records/freeman/adam");

此处的关键在于通过HTTP方法表达出你想让服务器做什么,而不是把它用某种方式编码进URL。

以这种方式使用HTTP方法的问题在于:许多主流的Web技术只支持GET和POST,而且不少防火墙只允许GET和POST请求通过。有一种惯用的做法可以规避这个限制,就是使用 X-HTTP-Method-Override标头来指定想要使用的HTTP方法,但形式上市在发送一个POST请求。代码演示如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example</title>
</head>
<body>
<div>
  <button>apples</button>
  <button>cherries</button>
  <button>bananas</button>
</div>
<div id="target">Press a button</div>

<script>
  var buttons = document.getElementsByTagName("button");
  for(var i = 0; i < buttons.length; i++){
    buttons[i].onclick = handleButtonPress;
  }

  var httpRequest;
  function handleButtonPress(e){
    httpRequest = new XMLHttpRequest();
    httpRequest.onreadystatechange = handleResponse;
    httpRequest.open("GET", e.target.innerHTML+".html");
    httpRequest.setRequestHeader("X-HTTP-Method-Override","DELETE");
    httpRequest.send();
  }

  function handleError(e){
    displayErrorMsg("Error event",httpRequest.status+httpRequest.statusText);
  }

  function handleResponse(){
    if(httpRequest.readyState == 4 && httpRequest.status == 200){
      document.getElementById("target").innerHTML = httpRequest.responseText;
    }
  }
</script>
</body>
</html>

在这个例子中,有使用XMLHttpRequest对象上的setRequestHeader方法来表明想让这个请求以HTTP DELETE方法的形式进行处理。请注意我在调用open方法之后才设置了这个标头。如果试图在open方法之前使用setRequestHeader方法,XMLHttpRequest对象就会抛出一个错误。

PS:覆盖HTTP需要服务器端的Web应用程序框架能理解X-HTTP-Method-Override这个惯例,并且你的服务器端应用程序要设置成能寻找和理解那些用的较少的HTTP方法。

 

4.2 禁用内容缓存

第二个可以添加到Ajax请求上的有用标头是Cache-Control,它在编写和调试脚本时尤其有用。一些浏览器会缓存通过Ajax请求所获得的内容,在浏览会话期间不会再请求它。对在前面的例子而言,意味着 apples.html、cherries.html和bananas.html 上的改动不会立即反映到浏览器中。下面代码展示了可以如何设置标头来避免这一点:

httpRequest = new XMLHttpRequest();
    httpRequest.onreadystatechange = handleResponse;
    httpRequest.open("GET", e.target.innerHTML+".html");
    httpRequest.setRequestHeader("Cache-Control","no-cache");
    httpRequest.send();

设置标头的方式和之前的例子一样,但这次用到的标头是 Cache-Control,而想要的值是 no-cache。放置这条语句后,如果通过Ajax请求的内容发生了改变,就会在下一次请求文档时体现出来。

 

4.3 读取响应标头

可以通过 getResponseHeader 和 getAllResponseHeaders 方法来读取服务器响应某个Ajax请求时发送的HTTP标头。在大多数情况下,你不需要关心标头里有什么,因为它们是浏览器和服务器之间交互事务的组成部分。下面代码展示了如何使用这个属性:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta content="width=device-width,user-scalable=no" name="viewport" />
  <meta name="author" content="叶超Luka" />
  <meta name="description" content="A simple example" />
  <title>Example</title>
  <link href="../img/ycdoit.ico" type="image/x-icon" rel="shortcut icon" />
  <style>
    #allheaders,#ctheader{border: medium solid black;padding: 2px;margin: 2px;}
  </style>
</head>
<body>
<div>
  <button>apples</button>
  <button>cherries</button>
  <button>bananas</button>
</div>
<div id="ctheader"></div>
<div id="allheaders"></div>
<div id="target">Press a button</div>

<script>
  var buttons = document.getElementsByTagName("button");
  for(var i = 0; i < buttons.length; i++){
    buttons[i].onclick = handleButtonPress;
  }

  var httpRequest;
  function handleButtonPress(e){
    httpRequest = new XMLHttpRequest();
    httpRequest.onreadystatechange = handleResponse;
    httpRequest.open("GET", e.target.innerHTML+".html");
    httpRequest.setRequestHeader("Cache-Control","no-cache");
    httpRequest.send();
  }

  function handleResponse(){
    if(httpRequest.readyState==2){
      document.getElementById("allheaders").innerHTML = httpRequest.getAllResponseHeaders();
      document.getElementById("ctheader").innerHTML = httpRequest.getResponseHeader("Content-Type");
    }else if(httpRequest.readyState == 4 && httpRequest.status == 200){
      document.getElementById("target").innerHTML = httpRequest.responseText;
    }
  }
</script>
</body>
</html>

效果图如下:

Javascript 基础---Ajax入门必看

根据此图可以看出开发服务器正在运行的Web服务器软件是 IntelliJ IDEA 15.0.4,最后修改 apples.html 文档的时间是6月27日(但屏幕截图是7月5日)。

 

Javascript 相关文章推荐
JS上传前预览图片实例
Mar 25 Javascript
关于jquery input textare 事件绑定及用法学习
Apr 03 Javascript
connect中间件session、cookie的使用方法分享
Jun 17 Javascript
JavaScript跨浏览器获取页面中相同class节点的方法
Mar 03 Javascript
Zero Clipboard实现浏览器复制到剪贴板的方法(多个复制按钮)
Mar 24 Javascript
30分钟快速掌握Bootstrap框架
May 24 Javascript
jQuery插件EasyUI实现Layout框架页面中弹出窗体到最顶层效果(穿越iframe)
Aug 05 Javascript
详解Angular4中路由Router类的跳转navigate
Jun 09 Javascript
浅谈angular4实际项目搭建总结
Dec 01 Javascript
JS加密插件CryptoJS实现的DES加密示例
Aug 16 Javascript
js键盘事件实现人物的行走
Jan 17 Javascript
JS实现放烟花效果
Mar 10 Javascript
jQuery获取浏览器类型和版本号的方法
Jul 05 #Javascript
jQuery事件委托之Safari
Jul 05 #Javascript
一道优雅面试题分析js中fn()和return fn()的区别
Jul 05 #Javascript
JS实现环形进度条(从0到100%)效果
Jul 05 #Javascript
JQuery组件基于Bootstrap的DropDownList(完整版)
Jul 05 #Javascript
结合代码图文讲解JavaScript中的作用域与作用域链
Jul 05 #Javascript
jQuery的Each比JS原生for循环性能慢很多的原因
Jul 05 #Javascript
You might like
预告映像公开!第1章续篇剧场版动画《Princess Principal Crown Handler》4月10日上映!
2020/03/06 日漫
php各种编码集详解和以及在什么情况下进行使用
2011/09/11 PHP
php foreach循环中使用引用的问题
2013/11/06 PHP
如何正确配置Nginx + PHP
2016/07/15 PHP
PHP验证终端类型是否为手机的简单实例
2017/02/07 PHP
JavaScript延迟加载
2021/03/09 Javascript
BOOM vs RR BO3 第一场2.13
2021/03/10 DOTA
JS操作Cookie写入和读取实例代码
2013/10/20 Javascript
Jqgrid表格随窗口大小改变而改变的简单实例
2013/12/28 Javascript
JavaScript函数学习总结以及相关的编程习惯指南
2015/11/16 Javascript
JQUERY的AJAX请求缓存里的数据问题处理
2016/02/23 Javascript
JS简单实现浮动窗口效果示例
2016/09/07 Javascript
基于JS实现限时抢购倒计时间表代码
2017/05/09 Javascript
记一次vue去除#问题处理经过小结
2019/01/24 Javascript
微信小程序 点击切换样式scroll-view实现代码实例
2019/10/11 Javascript
详解如何在Javascript和Sass之间共享变量
2019/11/13 Javascript
JS document对象简单用法完整示例
2020/01/14 Javascript
[03:14]2014DOTA2西雅图国际邀请赛 EG战队巡礼
2014/07/07 DOTA
Python获取网页上图片下载地址的方法
2015/03/11 Python
Django组件cookie与session的具体使用
2019/06/05 Python
深入理解Python 多线程
2020/06/16 Python
python实现文件分片上传的接口自动化
2020/11/19 Python
HTML5 Canvas锯齿图代码实例
2014/04/10 HTML / CSS
意大利男装网店:Vrients
2019/05/02 全球购物
教师岗位职责
2013/11/17 职场文书
《雪儿》教学反思
2014/04/17 职场文书
银行职员自我鉴定
2014/04/20 职场文书
市场营销工作计划书
2014/05/06 职场文书
学雷锋标兵事迹材料
2014/08/18 职场文书
十佳少先队员演讲稿
2014/09/12 职场文书
上课随便讲话检讨书
2014/09/12 职场文书
高中家长意见怎么写
2015/06/03 职场文书
消费者理赔投诉书
2015/07/02 职场文书
商务宴会祝酒词
2015/08/11 职场文书
python程序的组织结构详解
2021/12/06 Python
聊聊redis-dump工具安装问题
2022/01/18 Redis