420 likes | 655 Vues
Ajax 编程技术 第四章 Ajax 技术. 4.1 XMLHttpRequest 对象. 本章主要介绍技术是 XMLHttpRequest 对象,因为 Ajax 应用程序的中心就是它。同时,对于许多数网页开发的实际问题中,它也是最广泛适用的解决方案。 我们还将介绍该对象的 GET 和 POST 方法的使用。同时探讨用户使用此对象常见的错误,例如当试图让页面在所有服务器上都能运行时遇到的一些问题。 最后还介绍一种 Ajax 技术的替代方案及其示例。. 4.1 XMLHttpRequest 对象.
E N D
4.1 XMLHttpRequest对象 本章主要介绍技术是XMLHttpRequest对象,因为Ajax应用程序的中心就是它。同时,对于许多数网页开发的实际问题中,它也是最广泛适用的解决方案。 我们还将介绍该对象的GET和POST方法的使用。同时探讨用户使用此对象常见的错误,例如当试图让页面在所有服务器上都能运行时遇到的一些问题。 最后还介绍一种Ajax技术的替代方案及其示例。 4-2
4.1 XMLHttpRequest对象 XMLHttpRequest对象最初是作为IE5中的一个ActiveX控件出现的,随后Mozilla 1.0、Netscape7、Safari1.2和Opera7.60都将它纳入自身。 XMLHttpRequest对象在IE浏览器和非IE浏览器中实现方法不同。 XMLHttpRequest对象的作用在于,允许用脚本程序通过HTTP连接到服务器,而不比通过HTTP请求响应模型与服务器通信。 4-3
4.2 创建XMLHttpRequest对象 • 创建XMLHttpRequest对象 • 在IE7、Firefox、safari和Opera中创建该对象的JavaScript代码为: var xmlRequet = new XMLHttpRequest(); • 在IE5/6中代码为: var xmlRequest = new ActiveXObject(“Microsoft.XMLHTTP”); 注意,JavaScript区分大小写,如果大小写不正确,什么东西都创建不出来。 使用XMLHttpRequest对象的方式有两种,同步和异步。 4-4
4.2 创建XMLHttpRequest对象 • 同步使用XMLHttpRequest对象 按照下面模式,可以同步地XMLHttpRequest对象: • 创建对象; • 创建请求; • 发送请求。 这种模式与传统模式没有区别,用处不大,真正强大的地方在于异步地使用它。 4-5
4.2 创建XMLHttpRequest对象 • 异步使用XMLHttpRequest对象 异步使用XMLHttpRequest对象时,必须使用onreadystatechange事件调用该对象。在触发该事件后,必须在应用程序采取行动之前检查readyState属性的内容,因此使用模式应该是: • 创建该对象; • 设置readystatechange事件触发一个指定的函数; • 检查readyState属性,看数据是否准备就绪。 • 如果没有准备好,隔一段时间再次检查。因为数据没有下载完时,我们无法使用它的属性和方法。 • 如果已经准备好,就继续往下执行; • 打开请求; • 发送请求。 readystatechange事件的整个操作都是在后台执行,这样就能够异步使用XMLHttpRequest对象。 4-6
4.2 创建XMLHttpRequest对象 • readyState属性 readyState属性指出了XMLHttpRequest对象在发送/接收数据过程中所处的几个状态。XMLHttpRequest对象会经历5种不同的状态。 • 0:未初始化。对象已经创建,但还未初始化,即还没调用open方法; • 1:已打开。对象已经创建并初始化,但还未调用send方法; • 2:已发送。已经调用send 方法,但该对象正在等待状态码和头的返回; • 3:正在接收。已经接收了部分数据,但还不能使用该对象的属性和方法,因为状态和响应头不完整; • 4:已加载。所有数据接收完毕 4-7
4.2 创建XMLHttpRequest对象 • XMLHttpRequest对象的属性和方法 4-8
4.2 创建XMLHttpRequest对象 • 使用XMLHttpRequest对象示例 此示例使用此对象网页的实现动态显示。页面上有几个链接,分别点击,可以显示不同的文字或图片,或者清除显示的文字或图片。下面是程序: 4-10
4.2 创建XMLHttpRequest对象 // index.htm <html> <meta http-equiv="Pragma" CONTENT="no-catch"> <meta http-equiv="Expires" CONTENT="-1" /> <head> <script type="text/javascript" src="XHRequest.js"></script></head> <body> Ajax实例展示 <table border=0 cellpadding=0 cellspacing=0 style="font-size:10pt;"> <tr><td align=center> <table border=0 cellpadding=2 cellspacing=0 style='font-size:10pt;' align=center> <tr> <td><a href="#" onclick ="sendRequest('Contacts');return false;">显示联系我们</a></td> <td><a href="#" onclick ="sendRequest('Calendar');return false;">显示日历时间</a></td> <td><a href="#" onclick ="sendRequest('Adverts');return false;">显示广告图片</a></td> </tr><tr> <td><a href="#" onclick ="sendRequest('delContacts');return false;">清除联系我们</a></td> <td><a href="#" onclick ="sendRequest('delCalendar');return false;">清除日历时间</a></td> <td><a href="#" onclick ="sendRequest('delAdverts');return false;">清除广告图片</a></td> </tr></table> </td></tr> <tr><td id="box1" height=60></td></tr> <tr><td id="box2" height=50></td></tr> <tr><td id="box3" height=50></td></tr> </table> </body> </html> 4-11
4.2 创建XMLHttpRequest对象 // XHRequest.js var xHRObject = false; if (window.XMLHttpRequest) { xHRObject = new XMLHttpRequest();} else if (window.ActiveXObject) { xHRObject = new ActiveXObject("Microsoft.XMLHTTP"); } function sendRequest(data) { if (data=='delContacts') { box1.innerHTML=''; } else if (data=='delCalendar') { box2.innerHTML=''; } else if (data=='delAdverts') { box3.innerHTML=''; } else { var bodyofrequest = getBody(data); xHRObject.open("POST", "display.php", true); xHRObject.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xHRObject.onreadystatechange = getData; xHRObject.send(bodyofrequest); } } function getBody(data) { var argument = "value="; argument += encodeURIComponent(data) return argument; } function getData() { if (xHRObject.readyState == 4 && xHRObject.status == 200) { var serverText = xHRObject.responseText; if(serverText.indexOf('|' != -1)) { element = serverText.split('|'); document.getElementById(element[0]).innerHTML = element[1]; } } } 4-12
4.2 创建XMLHttpRequest对象 <?php //display.php switch($_REQUEST['value']) { case 'Contacts': echo "box1|<br><b>Contacts</b><br>Anhui, Hefei,USTC"; break; case 'Calendar': $dt = gmdate("M d Y H:i:s"); echo "box2|<br><b>Calendar:</b><br> $dt"; break; case 'Adverts': $source = "logo.gif"; echo "box3|<br><b>Advert:</b><br><img src='$source '>"; break; case 'delContacts': echo "box1| "; break; case 'delCalendar': echo "box2| "; break; case 'delAdverts': echo "box3| "; break; } ?> 4-13
4.2 创建XMLHttpRequest对象 • 程序运行:运行初始状态入图1,点击链接后见图2: 图2 图1 4-14
4.3 常见错误 • 编程常见错误 • 企图通过双击网页文件运行它; • XMLHttpRequest大小写不正确; • 多写了对圆括号: 正确:xHRObject.onreadystatechange =getData; 错误:xHRObject.onreadystatechange =getData(); 必须弄清楚,在JavaScript中: • 函数名后如果有圆括号,意思就是将函数的返回值赋给等号左边的变量; • 没有圆括号,是将函数本身赋给等号前的变量。 4-15
4.3 常见错误 • 同源问题 XMLHttpRequest对象有些问题来自于同源问题。在较早版本的浏览器中,可以运行来自任何源的任何脚本,由此带来很严重的安全隐患。因此,处于安全的考量,“同源策略”被要求强制执行。即只有来自同一域、同一协议和同一端口的脚本才可以运行。 IE不检验它从XMLHttpRequest对象中取回的字段。其中的一个字段就是HTTPREFERER,它包含用户所浏览页面的URL/域名(注意:该字段的值并不总是一个)。 这意味着Referer完全可以在客户端进行伪造。IE这个的这个安全漏洞Referer值不可信。解决的办法之一是,我们可以在编写Cookie时,将域名/服务器添加到cookie中,以便验证发出的和接收的同源。 4-16
4.3 常见错误 • 缓存控制:IE主动缓存 为了节约带宽资源,浏览器会在本地缓存页面,然后从缓存中找出该页面而不是从源服务器下载页面。 这样一来,当页面更新后,可能页面并没有显示这种更新。解决的办法是,强制停止缓存。可以在网页中插入如下代码: <meta http-equiv=“Pragma” CONTENT=“no-catch” /> <meta http-equiv=“Expires” CONTENT=“-1” /> 这样足以使浏览器重载该页面。但如果使用XMLHttpRequest对象,且请求中包含GET指令,那么IE将始终缓存该页面,而决不会重载该页面。 4-17
4.3 常见错误 • 缓存问题的解决方法 我们有三种办法来解决缓存造成的问题。 • 在GET请求后添加querystring,并确保每次运行时, querystring值都不一样。将日期作为querystring值是一个好主意: xHRObject.open(“GET”,”display.php?id=“ + Number(new Date)+”&value=“ + data, true); 这种“每次输入不同的querystring值”的解决方法,从原理上将是一种回避策略。 4-18
4.3 常见错误 • 设置HTTP头部的If-Modified-Since为一个过期的时间: xHRObject.open=(“GET”, “display.php?value=“+data, true); xHRObject.setRequestHeader(“If-Modified-Since”, “Sat,1, Jan 2000 00:00:00 GMT”); 使用这种方法,可以阻止缓存。 • 使用POST 请求。我们将在下一节讨论这种方法。 一般来说,前两种方法用起来比较顺手,第3种方法可以完全避免缓存的困扰。 4-19
4.3 常见错误 • 跨浏览器兼容 在使用Ajax技术时,最困难的问题是让应用程序在不同的浏览器中都能够正常运行。实际上,这种想法非常不实际。 爱XMLHttpRequest应用中,用户使用的是IE还是Mozilla浏览器已成为次要问题,主要问题是创建哪个版本浏览器的XMLHttpRequest对象。我们需要注意以下问题: • ActiveX控件不能使用在IE之外的浏览器中; • 动态HTTPjihe document.all只能在IE上工作; • 在某些版本的Firefox上运行XMLHttpRequest会崩溃; • IE不区别大小写,而Mozilla却区别大小写; • 不同的IE版本,必须调用不同版本的MSXML。 • … 4-20
4.4 POST方法 使用POST方法替代GET方法,是另一种解决IE主动缓存页面的办法,它使Ajax技术提供一个更加无缝的前端。 将GET方法变成POST方法,除了需要做: • 将querystring删除; • 对发送的数据编码; • 将它作为跨服务器参数发送给send方法。该参数仍然是名/值,与querystring类似,格式为:value=Contents。但不附加在URL中,而是使用URL编码。 4-21
4.4 POST方法 • 如4.2节的示例中的POST方法: function sendRequest(data) { if (data=='delContacts') { box1.innerHTML=''; } else if (data=='delCalendar') { box2.innerHTML=''; } else if (data=='delAdverts') { box3.innerHTML=''; } else { var bodyofrequest = getBody(data); xHRObject.open(“POST”, “display.php”, true); //GET变为POST,删除querystring参数 xHRObject.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xHRObject.onreadystatechange = getData; xHRObject.send(bodyofrequest); } } function getBody(data) { var argument = "value="; argument += encodeURIComponent(data)//上传的数据编码,但变量名不编码 return argument; } 4-22
4.4 POST方法 • POST方法和GET方法的比较 • POST方法比GET方法传递的信息量大,最多可达2GB,而GET方法则大为减小,IE限制为2083个字符,Opera为4050个字符,Netscape4为8192个字符 • GET方法只能使用ASCII码传送且有缓存的困扰;而POST使用编码传送,且没有缓存的困扰。 • 如何选则 • 当查询的结果不会导致客户端页面变化,或下载量较小,使用GET方法; • 当查询的结果会导致客户端页面变化,或下载量很大时,用POST方法。 4-23
4.5 其他Ajax技术 • 动态脚本加载 可以用另一种不错的方法来替代XMLHttpRequest对象的使用。这就是动态脚本加载技术。 利用此技术,可以使用DOM动态创建JavaScript脚本,SRC属性也可以动态赋值。JavaScript源文件只在将其添加到页面时才下载并执行。 • 概念: • 添加脚本到页面; • 该脚本动态添加到另一个脚本到页面,并在后面附加SRC属性; • 脚本使用服务器启动对话框 4-24
4.5 其他Ajax技术 • 示例 示例中使用了一个脚本,根据用户选择,动态地创建其它3个脚本中的一个。 为了简捷,我们不使用服务器启动任何对话框,因为后面会介绍这种方法还有一些不足。 4-25
4.5 其他Ajax技术 • 创建一个名为ScriptLoader.htm的HTML页面: // ScriptLoader.htm <html xmlns="http://www.w3.org/1999/xhtml" > <head> <script type="text/javascript" src="ScriptLoader.js"></script> </head> <body> 你想加载哪个脚本?<br/> 脚本1<input id="range“ name="range" value="1" type="radio" onclick="retrieveInfo('1')" /><br/> 脚本2<input id="Radio1" name="range" value="2" type="radio" onclick="retrieveInfo('2')" /><br/> 脚本3<input id="Radio2" name="range" value="3" type="radio" onclick="retrieveInfo('3')"/><br/> </body> </html> 4-26
4.5 其他Ajax技术 • 创建名为ScriptLoader.js的脚本: // ScriptLoader.js function retrieveInfo(data) { var newScript = document.createElement("script"); newScript.src = "script" + data + ".js"; document.body.appendChild(newScript); } 4-27
4.5 其他Ajax技术 • 分别创建3个脚本,名为Script1.js, Script2.js, Script3.js //Script1.js alert ("加载了脚本1"); //Script2.js alert ("加载了脚本2"); //Script3.js alert (“这是脚本3"); 4-28
4.5 其他Ajax技术 • 现在运行ScriptLoader.htm文件,单击第二个按钮,程序运行结果如下图所示: 4-29
4.5 其他Ajax技术 • 说明 程序关键语句如下: var newScript = document.createElement(“script”); // 创建Script元素 newScript.src = “script” + data + “.js”; // 设置SRC 属性 document.body.appendChild(newScript); // 将新元素附加在body元素上 4-30
4.5 其他Ajax技术 • 评价 • 优点: • 可以使用多个不同的脚本,并按需加载不同的脚本; • 为我们提供了另一个创建服务器调用的机会。 • 缺点: • IE中动态加载脚本会停止其他所有的处理; • 只能使用GET方法,不能使用POST方法; • 往往不知道脚本是否得到真正的加载。 4-31
4.5 其他Ajax技术 • 图象和cookie • 工作方式 这种模式的工作方式如下: • <img>元素使用src属性封装请求,传递附加在querystring后的任何其他信息; • 服务器存储该信息,并编写存储在客户端cookie中的唯一可识别信息。 这种模式与动态脚本加载示例非常相似。但是没有动态脚本加载的缺点,它被广泛用于拥有大量电子邮件的用户,或者希望跟踪用户浏览习惯的网站。 4-32
4.5 其他Ajax技术 • 示例 网上售书页面。当用户查看该页面时,其正在浏览的页面的相关信息会作为图象的一部分发送到服务器,然后服务器会编写唯一的表示符到cookie,并使用消息框向用户显示该cookie中包含的信息。 4-33
4.5 其他Ajax技术 • 创建Cataloque.htm <html xmlns="http://www.w3.org/1999/xhtml" > <head> <script type="text/javascript" src="ImageLoader.js"></script> </head> <body onload="createImage()" style="font-size:10pt;"> <b>Book:</b><br/> <img id="cover" src="ajax.jpg" /> <br /><br /> <b>作者: </b><span id="authors"> 许富</span> <br /><b>ISBN: </b><span id="ISBN">97-7-123456</span> <br /><b>定价: </b><span id="price">50.20元</span> <img id="secret" src="onebyone.gif" /><br /><br /> <input type="button" onclick="showCookie()" value="查看cookie" /> </body> </html> 4-34
4.5 其他Ajax技术 • 创建ImageLoader.js脚本: function createImage() { var bookid = document.getElementById("ISBN").innerHTML; var img = document.getElementById("secret"); img.src = "relayInfo.php?bookid=" + bookid; img.width = 0; img.height = 0; } function showCookie() { var cookie = getCookieInfo("AnonymousID"); alert(cookie); } function getCookieInfo(cookie) { RegularXp = "(?:; )?" + cookie + "=([^;]*);?"; var RegularXpExtract = new RegExp(RegularXp); if (RegularXpExtract.test(document.cookie)) { return decodeURIComponent(RegExp["$1"]); } else { return null; } } 4-35
4.5 其他Ajax技术 • 创建relayInfo.php: <?php if ($_COOKIE[AnonymousID]) { $tempCookie = $_COOKIE["AnonymousID"]; setcookie("AnonymousID", $tempCookie."|BOOKID:".$_GET["bookid"], time()+3600); } else { $random_id = (rand()%9999999); $tempCookie = "USERID:" .$random_id."|BOOKID:" . $_GET["bookid"]; setcookie("AnonymousID", $tempCookie."|BOOKID:".$_GET["bookid"], time()+3600); } ?> 4-36
4.5 其他Ajax技术 • 运行结果 • 打开浏览器,运行首 文件Cataloque.htm, 见 右图。 • 点击“查看cookie”, 屏幕出现对话框,显示 Cookie信息 • 关闭浏览器,再次打 开,cookie信息被追加。 4-37
4.5 其他Ajax技术 • 示例说明 本例展示出,如何通过动态改变图象的scr属性来调用服务器。 • 在页面的开始部分加载了createImage,然后找到包含该图书ISBN的<span>元素中的内容(该书内容被存储在cookie中),以及Ajax.jpg图片 var bookid = document.getElementById(“ISBN”).innerHTML; var img = document.getElementById(“secret”); • 接着,使用指向服务器端页面和作为querystring传递的图书ISBN替换掉src属性。 img.src = "relayInformation.php?bookid=" + bookid; img.width = 0; img.height = 0; 4-38
4.5 其他Ajax技术 • 服务器端代码仅用来检查cookie是否存在。如果不存在cookie,则添加一个唯一的ID(本例中是一个随机数)和图书的ISBN号;如果存在cookie,则在cookie尾部添加上述信息。 if ($_COOKIE[AnonymousID]) { $tempCookie = $_COOKIE["AnonymousID"]; setcookie("AnonymousID", $tempCookie."|BOOKID:".$_GET["bookid"], time()+3600); } else { $random_id = (rand()%9999999); $tempCookie = "USERID:" .$random_id."|BOOKID:" . $_GET["bookid"]; setcookie("AnonymousID", $tempCookie."|BOOKID:".$_GET["bookid"], time()+3600); } 4-39
4.5 其他Ajax技术 • 然后使用showCookie()函数显示内容。在这里,Ajax技术快速得到匿名用户的查看模式。所以,利用该ID存储数据没有问题,然后就可以慢慢收集用户浏览习惯。 4-40
4.5 其他Ajax技术 • 评价 • 缺点: • 如果用户选择关闭图片下载,就没法运行; • Cookie存储信息有限制,大约是4KB; • 只能使用GET方法,不能使用POST方法。 4-41
4.5 其他Ajax技术 • 隐藏框架 • 原理:常使用此技术用于返回服务器信息。它采用带有两个框架的标准框架,打开两个独立的页面,第一个页面可见,第二个页面宽度和高度设置为0而变成隐藏不可见。隐藏框架用于发送请求和接收来自服务器的响应数据,但只在需要时才将接收的数据放在可见的框架中显示。 4-42