420 likes | 585 Views
Ajax 编程技术 第九章 常见问题解决 模式. 9.1 设计模式背景知识. 设计模式是解决特定的常见问题的成熟技术或方法。我们会遇到成千上万的模式问题,不可能逐一介绍,本章只介绍采用 Ajax 技术来解决这些问题的常见方法。它们是: 表单验证设计模式; 鼠标悬停 (mouseover) 模式; 刷新模式; 错误处理模式。. 9.2 表单验证. 表单验证用于过滤无意义或恶意输入数据,保持系统安全。 问题 传统的验证用户输入的内容方法是,等待用户输入整页数据提交后,在服务器验证。响应很慢。如果只在客户端验证,就不能访问数据库中的细节数据。
E N D
9.1 设计模式背景知识 设计模式是解决特定的常见问题的成熟技术或方法。我们会遇到成千上万的模式问题,不可能逐一介绍,本章只介绍采用Ajax技术来解决这些问题的常见方法。它们是: • 表单验证设计模式; • 鼠标悬停(mouseover)模式; • 刷新模式; • 错误处理模式。 9-2
9.2 表单验证 表单验证用于过滤无意义或恶意输入数据,保持系统安全。 • 问题 传统的验证用户输入的内容方法是,等待用户输入整页数据提交后,在服务器验证。响应很慢。如果只在客户端验证,就不能访问数据库中的细节数据。 解决的办法是同时在服务器和客户端进行验证。这就用到Ajax技术。 • 模式 有两种模式可用于表单验证: • 当值被改变,或字段焦点丢失时,提交此字段; • 定期提交要验证的字段内容。 9-3
9.2 表单验证 • 示例:字段焦点丢失时提交数据 • 先在默认目录下创建名为mydata.mdb的access数据库,库中有一个名为users的表,字段有2个: • ID字段:自动编号; • UserName字段:文本类型,40个字符宽。 假设表中已经输入用户姓名有:Peter, Alex, Kate 9-4
9.2 表单验证 • 创建主文件index.htm <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>Form Validation Example</title> <link id="Link1" rel="stylesheet" href="FormValidation.css" type="text/css" /> <script type="text/javascript" src="FormValidation.js"></script> </head> <body> <form name="form1" id="form1" method="post" action="formcheck.php"> User Name: <input id="UserName" type="text" onblur="Validate('UserName')" /> <br/> <span id="span"></span> <br/><br /> Address:<input id="Address" class="textbox" type="text" /> <br /><br /> <input type="button" value="Click here to submit details" /> </form> </body> </html> 9-5
9.2 表单验证 • FormValidation.js文件: var xHRObject = false; if (window.XMLHttpRequest) { xHRObject = new XMLHttpRequest(); } else if (window.ActiveXObject) { xHRObject = new ActiveXObject("Microsoft.XMLHTTP"); } function getData() { if ((xHRObject.readyState == 4) && (xHRObject.status == 200)) { var serverText = xHRObject.responseText; if (serverText == "True") { span.innerHTML = "This user name has already been taken"; } if (serverText == "False") { span.innerHTML = "This user name is available"; } } } function getBody(newform, data) { var argument = data + "="; argument += encodeURIComponent(document.getElementById(data).value) return argument; } function Validate(data) { var newform = document.forms[0]; var bodyofform = getBody(newform, data); if (bodyofform != "UserName=") { xHRObject.open("POST", "Validate.php", true); xHRObject.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xHRObject.onreadystatechange = getData; xHRObject.send(bodyofform); ; } else { span.innerHTML = "Blank user names not allowed"; } } 9-6
9.2 表单验证 • 创建FormValidation.css文件: .textbox { position: absolute; left: 100px; } span#span { color: Red; } 9-7
9.2 表单验证 • 创建Validate.php文件: <?php $conn = new COM('ADODB.Connection') or exit('Cannot start ADO.'); $rs = new COM('ADODB.Recordset') or die('Coult not make rs'); $connstring = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=".realpath('mydata.mdb').";"; $conn->Open($connstring); $sql = 'SELECT * from Users'; $rs->Open($sql, $conn, 1, 3); $rs = $conn->Execute($sql); $check = "False"; while (!$rs->EOF) { while (!$rs->EOF) { if ($rs->Fields['UserName']->value == $_POST["UserName"]) { $check = "True"; } $rs->MoveNext(); } } $rs->Close(); $conn->Close(); $rs=null; $conn=null; echo $check; ?> 9-8
9.2 表单验证 • 程序运行:用户姓名输入:Peter,服务器数据库中已经有此人姓名,不能再注册此姓名,所以输入无效: 9-9
9.2 表单验证 • 但如果输入库中没有的用户名zhangsan, 则验证合格,输入通过。 9-10
9.3 鼠标悬停模式 鼠标悬停技术是Web页面上动态显示信息的常见方法,它不影响原始页面上信息的布局而动态显示某项的附加信息。 • 问题 如何在不影响外观格局,不干扰用户当前活动的情况下,显示关于某项的附加信息。这是鼠标悬停技术所擅长的本事。 • 模式 可以利用弹出部分透明框架的形式显示关于特定项的附加信息,而不会造成中断。部分透明是指页面上的原始信息不会变得不明显。 9-11
9.3 鼠标悬停模式 • 鼠标悬停示例 一网页介绍5个旅游目的地。当鼠标悬停在风景地照片上,会得到前往该旅游风景地的详细信息。 此示例使用了boxover.js,严格来说,这里并不需要Ajax,然而此示例嫁接了使用XML文件的Ajax代码,它通过旅游地组团代号TarId,来确定鼠标悬停在哪张风景地图片上。并返回该风景地的详细(旅游目的地,最佳旅游时间、报价等)。 9-12
9.3 鼠标悬停模式 • 主页程序index.htm: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>Mouse Over Example</title> <script type="text/javascript" src="boxover.js"></script> <script type="text/javascript" src="mouse.js"></script> </head> <body> <table align=center> <tr> <td colspan=4 align=center>旅游推介</br> </tr> <tr> <td><a title="header=[<img src='01.jpg'/>] body=[01]'" href='c1.htm'><img src="01.jpg" border=0 /></a></td> <td><a title="header=[<img src='02.jpg'/>] body=[02]'" href='c2.htm'><img src="02.jpg" border=0 /></a></td> <td><a title="header=[<img src='03.jpg'/>] body=[03]'" href='c3.htm'><img src="03.jpg" border=0 /></a></td> <td><a title="header=[<img src='04.jpg'/>] body=[04]'" href='c4.htm'><img src="04.jpg" border=0 /></a></td> <td><a title="header=[<img src='05.jpg'/>] body=[05]'" href='c5.htm'><img src="05.jpg" border=0 /></a></td> </tr> </table> </body> </html> 9-13
9.3 鼠标悬停模式 • boxover.js: if (typeof document.attachEvent!='undefined') { window.attachEvent('onload',init); document.attachEvent('onmousemove',moveMouse); document.attachEvent('onclick',checkMove); } else { window.addEventListener('load',init,false); document.addEventListener('mousemove',moveMouse,false); document.addEventListener('click',checkMove,false); } var oDv=document.createElement("div"); var dvHdr=document.createElement("div"); var dvBdy=document.createElement("div"); var windowlock,boxMove,fixposx,fixposy,lockX,lockY,fixx,fixy,ox,oy,boxLeft,boxRight,boxTop,boxBottom,evt,mouseX,mouseY,boxOpen,totalScrollTop,totalScrollLeft; boxOpen=false; ox=10;oy=10; lockX=0;lockY=0; function init() { oDv.appendChild(dvHdr); oDv.appendChild(dvBdy); oDv.style.position="absolute"; oDv.style.visibility='hidden'; document.body.appendChild(oDv); } 9-14
9.3 鼠标悬停模式 function checkElemBO(txt) { if (!txt || typeof(txt) != 'string') return false; if ((txt.indexOf('header')>-1)&&(txt.indexOf('body')>-1)&&(txt.indexOf('[')>-1)&&(txt.indexOf('[')>-1)) return true; else return false; } function scanBO(curNode) { if (checkElemBO(curNode.title)) { curNode.boHDR=getParam('header',curNode.title); curNode.boBDY=getParam('body',curNode.title); curNode.boCSSBDY=getParam('cssbody',curNode.title); curNode.boCSSHDR=getParam('cssheader',curNode.title); curNode.IEbugfix=(getParam('hideselects',curNode.title)=='on')?true:false; curNode.fixX=parseInt(getParam('fixedrelx',curNode.title)); curNode.fixY=parseInt(getParam('fixedrely',curNode.title)); curNode.absX=parseInt(getParam('fixedabsx',curNode.title)); curNode.absY=parseInt(getParam('fixedabsy',curNode.title)); curNode.offY=(getParam('offsety',curNode.title)!='')?parseInt(getParam('offsety',curNode.title)):10; curNode.offX=(getParam('offsetx',curNode.title)!='')?parseInt(getParam('offsetx',curNode.title)):10; curNode.fade=(getParam('fade',curNode.title)=='on')?true:false; curNode.fadespeed=(getParam('fadespeed',curNode.title)!='')?getParam('fadespeed',curNode.title):0.04; curNode.delay=(getParam('delay',curNode.title)!='')?parseInt(getParam('delay',curNode.title)):0; if (getParam('requireclick',curNode.title)=='on') { curNode.requireclick=true; document.all?curNode.attachEvent('onclick',showHideBox):curNode.addEventListener('click',showHideBox,false); document.all?curNode.attachEvent('onmouseover',hideBox):curNode.addEventListener('mouseover',hideBox,false); } function defHdrStyle() { dvHdr.innerHTML='<img style="vertical-align:middle" src="info.gif"> '+dvHdr.innerHTML; dvHdr.style.fontWeight='bold'; dvHdr.style.width='150px'; dvHdr.style.fontFamily='arial'; dvHdr.style.border='1px solid #A5CFE9'; dvHdr.style.padding='3px'; dvHdr.style.fontSize='11px'; dvHdr.style.color='#4B7A98'; dvHdr.style.background='#D5EBF9'; dvHdr.style.filter='alpha(opacity=85)'; // IE dvHdr.style.opacity='0.85'; // FF } function defBdyStyle() { dvBdy.style.borderBottom='1px solid #A5CFE9'; dvBdy.style.borderLeft='1px solid #A5CFE9'; dvBdy.style.borderRight='1px solid #A5CFE9'; dvBdy.style.width='150px'; dvBdy.style.fontFamily='arial'; dvBdy.style.fontSize='11px'; dvBdy.style.padding='3px'; dvBdy.style.color='#1B4966'; dvBdy.style.background='#FFFFFF'; dvBdy.style.filter='alpha(opacity=85)'; // IE dvBdy.style.opacity='0.85'; // FF } 9-15
9.3 鼠标悬停模式 else { // Note : if requireclick is on the stop clicks are ignored if (getParam('doubleclickstop',curNode.title)!='off') { document.all?curNode.attachEvent('ondblclick',pauseBox):curNode.addEventListener('dblclick',pauseBox,false); } if (getParam('singleclickstop',curNode.title)=='on') { document.all?curNode.attachEvent('onclick',pauseBox):curNode.addEventListener('click',pauseBox,false); } } curNode.windowLock=getParam('windowlock',curNode.title).toLowerCase()=='off'?false:true; curNode.title=''; curNode.hasbox=1; } else curNode.hasbox=2; } function getParam(param,list) { var reg = new RegExp('([^a-zA-Z]' + param + '|^' + param + ')\\s*=\\s*\\[\\s*(((\\[\\[)|(\\]\\])|([^\\]\\[]))*)\\s*\\]'); var res = reg.exec(list); var returnvar; if (param != "body") { if (res) return res[2].replace('[[','[').replace(']]',']'); else return ''; } else { if (res) { kink = mousebox(res[2].replace('[[','[').replace(']]',']')); return kink; } else return ''; } } 9-16
function getParam(param,list) { var reg = new RegExp('([^a-zA-Z]' + param + '|^' + param + ')\\s*=\\s*\\[\\s*(((\\[\\[)|(\\]\\])|([^\\]\\[]))*)\\s*\\]'); var res = reg.exec(list); var returnvar; if (param != "body") { if (res) return res[2].replace('[[','[').replace(']]',']'); else return ''; } else { if (res) { kink = mousebox(res[2].replace('[[','[').replace(']]',']')); return kink; } else return ''; } } function Left(elem){ var x=0; if (elem.calcLeft) return elem.calcLeft; var oElem=elem; while(elem){ if ((elem.currentStyle)&& (!isNaN(parseInt(elem.currentStyle.borderLeftWidth)))&&(x!=0)) x+=parseInt(elem.currentStyle.borderLeftWidth); x+=elem.offsetLeft; elem=elem.offsetParent; } oElem.calcLeft=x; return x; } function Top(elem){ var x=0; if (elem.calcTop) return elem.calcTop; var oElem=elem; while(elem){ if ((elem.currentStyle)&& (!isNaN(parseInt(elem.currentStyle.borderTopWidth)))&&(x!=0)) x+=parseInt(elem.currentStyle.borderTopWidth); x+=elem.offsetTop; elem=elem.offsetParent; } oElem.calcTop=x; return x; } var ah,ab; function applyStyles() { if(ab) oDv.removeChild(dvBdy); if (ah) oDv.removeChild(dvHdr); dvHdr=document.createElement("div"); dvBdy=document.createElement("div"); CBE.boCSSBDY?dvBdy.className=CBE.boCSSBDY:defBdyStyle(); CBE.boCSSHDR?dvHdr.className=CBE.boCSSHDR:defHdrStyle(); dvHdr.innerHTML=CBE.boHDR; dvBdy.innerHTML=CBE.boBDY; ah=false; ab=false; if (CBE.boHDR!='') { oDv.appendChild(dvHdr); ah=true; } if (CBE.boBDY!=''){ oDv.appendChild(dvBdy); ab=true; } }
// Customised function for inner window dimension function SHW() { if (document.body && (document.body.clientWidth !=0)) { width=document.body.clientWidth; height=document.body.clientHeight; } if (document.documentElement && (document.documentElement.clientWidth!=0) && (document.body.clientWidth + 20 >= document.documentElement.clientWidth)) { width=document.documentElement.clientWidth; height=document.documentElement.clientHeight; } return [width,height]; } var ID=null; function moveMouse(e) { //boxMove=true; e?evt=e:evt=event; CSE=evt.target?evt.target:evt.srcElement; if (!CSE.hasbox) { // Note we need to scan up DOM here, some elements like TR don't get triggered as srcElement iElem=CSE; while ((iElem.parentNode) && (!iElem.hasbox)) { scanBO(iElem); iElem=iElem.parentNode; } } if ((CSE!=LSE)&&(!isChild(CSE,dvHdr))&&(!isChild(CSE,dvBdy))){ if (!CSE.boxItem) { iterElem=CSE; while ((iterElem.hasbox==2)&&(iterElem.parentNode)) iterElem=iterElem.parentNode; CSE.boxItem=iterElem; } iterElem=CSE.boxItem; if (CSE.boxItem&&(CSE.boxItem.hasbox==1)) { LBE=CBE; CBE=iterElem; if (CBE!=LBE) { applyStyles(); if (!CBE.requireclick) if (CBE.fade) { if (ID!=null) clearTimeout(ID); ID=setTimeout("fadeIn("+CBE.fadespeed+")",CBE.delay); } else { if (ID!=null) clearTimeout(ID); COL=1; ID=setTimeout("oDv.style.visibility='visible';ID=null;",CBE.delay); } if (CBE.IEbugfix) {hideSelects();} fixposx=!isNaN(CBE.fixX)?Left(CBE)+CBE.fixX:CBE.absX; fixposy=!isNaN(CBE.fixY)?Top(CBE)+CBE.fixY:CBE.absY; lockX=0; lockY=0; boxMove=true; ox=CBE.offX?CBE.offX:10; oy=CBE.offY?CBE.offY:10; } }
else if (!isChild(CSE,dvHdr) && !isChild(CSE,dvBdy) && (boxMove)) { // The conditional here fixes flickering between tables cells. if ((!isChild(CBE,CSE)) || (CSE.tagName!='TABLE')) { CBE=null; if (ID!=null) clearTimeout(ID); fadeOut(); showSelects(); } } LSE=CSE; } else if (((isChild(CSE,dvHdr) ||isChild(CSE,dvBdy))&&(boxMove))) { totalScrollLeft=0; totalScrollTop=0; iterElem=CSE; while(iterElem) { if(!isNaN(parseInt(iterElem.scrollTop))) totalScrollTop+=parseInt(iterElem.scrollTop); if(!isNaN(parseInt(iterElem.scrollLeft))) totalScrollLeft+=parseInt(iterElem.scrollLeft); iterElem=iterElem.parentNode; } if (CBE!=null) { boxLeft=Left(CBE)-totalScrollLeft; boxRight=parseInt(Left(CBE)+CBE.offsetWidth)-totalScrollLeft; boxTop=Top(CBE)-totalScrollTop; boxBottom=parseInt(Top(CBE)+CBE.offsetHeight)-totalScrollTop; doCheck(); } } if (boxMove&&CBE) { // This added to alleviate bug in IE6 w.r.t DOCTYPE bodyScrollTop=document.documentElement&&document.documentElement.scrollTop?document.documentElement.scrollTop:document.body.scrollTop; bodyScrollLet=document.documentElement&&document.documentElement.scrollLeft?document.documentElement.scrollLeft:document.body.scrollLeft; mouseX=evt.pageX?evt.pageX-bodyScrollLet:evt.clientX-document.body.clientLeft; mouseY=evt.pageY?evt.pageY-bodyScrollTop:evt.clientY-document.body.clientTop; if ((CBE)&&(CBE.windowLock)) { mouseY < -oy?lockY=-mouseY-oy:lockY=0; mouseX < -ox?lockX=-mouseX-ox:lockX=0; mouseY > (SHW()[1]-oDv.offsetHeight-oy)?lockY=-mouseY+SHW()[1]-oDv.offsetHeight-oy:lockY=lockY; mouseX > (SHW()[0]-dvBdy.offsetWidth-ox)?lockX=-mouseX-ox+SHW()[0]-dvBdy.offsetWidth:lockX=lockX; } oDv.style.left=((fixposx)||(fixposx==0))?fixposx:bodyScrollLet+mouseX+ox+lockX+"px"; oDv.style.top=((fixposy)||(fixposy==0))?fixposy:bodyScrollTop+mouseY+oy+lockY+"px"; } } function doCheck() { if ( (mouseX < boxLeft) || (mouseX >boxRight) || (mouseY < boxTop) || (mouseY > boxBottom)) { if (!CBE.requireclick) fadeOut(); if (CBE.IEbugfix) {showSelects();} CBE=null; } }
function pauseBox(e) { e?evt=e:evt=event; boxMove=false; evt.cancelBubble=true; } function showHideBox(e) { oDv.style.visibility=(oDv.style.visibility!='visible')?'visible':'hidden'; } function hideBox(e) { oDv.style.visibility='hidden'; } var COL=0; var stopfade=false; function fadeIn(fs) { ID=null; COL=0; oDv.style.visibility='visible'; fadeIn2(fs); } function fadeIn2(fs) { COL=COL+fs; COL=(COL>1)?1:COL; oDv.style.filter='alpha(opacity='+parseInt(100*COL)+')'; oDv.style.opacity=COL; if (COL<1) setTimeout("fadeIn2("+fs+")",20); } function fadeOut() { oDv.style.visibility='hidden'; } function isChild(s,d) { while(s) { if (s==d) return true; s=s.parentNode; } return false; } var cSrc; function checkMove(e) { e?evt=e:evt=event; cSrc=evt.target?evt.target:evt.srcElement; if ((!boxMove)&&(!isChild(cSrc,oDv))) { fadeOut(); if (CBE&&CBE.IEbugfix) {showSelects();} boxMove=true; CBE=null; } } function showSelects(){ var elements = document.getElementsByTagName("select"); for (i=0;i< elements.length;i++){ elements[i].style.visibility='visible'; } } function hideSelects(){ var elements = document.getElementsByTagName("select"); for (i=0;i< elements.length;i++){ elements[i].style.visibility='hidden'; } }
9.3 鼠标悬停模式 • 创建mouse.js: var tarid = null; var http = null; function mousebox(tarid) { if (window.ActiveXObject) { http = new ActiveXObject("Microsoft.XMLHTTP"); } else if (window.XMLHttpRequest) { http = new XMLHttpRequest(); } http.open("GET", "Catalogue.xml", false); http.send(null); var xml = http.responseXML; //Load XSL if (window.ActiveXObject) { var xsl = new ActiveXObject("MSXML2.FreeThreadedDomDocument.3.0"); xsl.async = false xsl.load("Catalogue.xsl") var template = new ActiveXObject("MSXML2.XSLTemplate") template.stylesheet = xsl processor = template.createProcessor() processor.input = xml processor.addParameter("TarId", tarid) processor.transform() //Transform return processor.output; } else { var xsltProcessor = new XSLTProcessor(); //Load XSL http = new XMLHttpRequest(); http.open("GET", "Catalogue.xsl", false); http.send(null); xslStylesheet = http.responseXML; xsltProcessor.importStylesheet(xslStylesheet); xsltProcessor.setParameter(null, "TarId", tarid); //Transform var fragment = xsltProcessor.transformToFragment(xml, document); return new XMLSerializer().serializeToString(fragment); } } 9-21
9.3 鼠标悬停模式 • 创建catalogue.xml: <?xml version="1.0" encoding="gb2312" ?> <catalog> <trip> <Target>甘肃敦皇</Target> <goTime>夏季</goTime><TarId>02</TarId><Price>¥4000</Price> </trip> <trip> <Target>吉林长春</Target><goTime>冬季</goTime><TarId>03</TarId><Price>¥2000</Price> </trip> <trip> <Target>内蒙海拉尔</Target><goTime>夏季</goTime><TarId>01</TarId><Price>¥4000</Price> </trip> <trip> <Target>四川九寨沟</Target><goTime>四季</goTime><TarId>04</TarId><Price>¥4500</Price> </trip> <trip> <Target>陕西西安</Target><goTime>四季</goTime><TarId>05</TarId><Price>¥3000</Price> </trip> </catalog> 9-22
9.3 鼠标悬停模式 • 创建catalogue.xsl: <?xml version="1.0" encoding="gb2312"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" indent="yes" version="4.0" /> <xsl:param name="TarId" /> <xsl:template match="/"> <xsl:for-each select="//trip"> <xsl:if test="TarId=$TarId"> <span> <b>旅游线路</b> : <xsl:value-of select="Target"/> </span><br/> <span> <b>最佳时间</b> : <xsl:value-of select="goTime"/> </span><br/> <span> <b>组团代号</b> : <xsl:value-of select="TarId"/> </span><br/> <span> <b>成行报价</b> : <xsl:value-of select="Price"/> </span><br/> </xsl:if> </xsl:for-each> </xsl:template> </xsl:stylesheet> 9-23
9.3 鼠标悬停模式 • 运行程序: 9-24
9.3 鼠标悬停模式 • 运行程序:鼠标悬停在第4幅图上,立即显示该风景地详细信息 9-25
9.4 轮询服务模式 有时候,我们希望页面能自动定时地轮询更新,而不是手动刷新。这就是我们本节要介绍的技术。 • 问题 如何能定期地检查服务器上的信息?如果使用具有GET方法的XMLHttpRequest对象,则IE会缓存调用的XML页面,达不到轮询服务器定期自动更新的目的。这是我们面临的问题 • 解决模式 这种异步行为叫轮询服务器。这种模式被称为页面流(page streaming)或服务流(service streaming)。 方法是,创建持续运行调用服务器上数据的脚本。一般程序在第一次调用后都会退出程序,在此模式中,程序会循环调用函数,然后等待一定的时间,再调用这些函数。 9-26
9.4 轮询服务模式 • 例:创建一个包含股票价格的XML文件,当我们在后台修改了XML文件中的股票价格后,经过下一轮询问,变动的股票价格会自动显示在页面中: • 创建主文件polling.htm: <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>Polling The Server Example</title> <link id="Link1" rel="stylesheet" href="Polling.css" type="text/css" /> <script type="text/javascript" src="polling.js"></script> </head> <body onload="getDocument()"> <span id="Stocks"></span> </body> </html> 9-27
9.4 轮询服务模式 • 创建Polling.js脚本文件: var xHRObject = false; if (window.XMLHttpRequest) { xHRObject = new XMLHttpRequest(); } else if (window.ActiveXObject) { xHRObject = new ActiveXObject("Microsoft.XMLHTTP"); } function loadDocument(fileName) { var xmlDoc = null if (window.ActiveXObject) { xmlDoc = new ActiveXObject("MSXML2.DOMDocument.3.0") } else { document.implementation.createDocument("","",null); } xmlDoc.async = false; xmlDoc.load(fileName); return xmlDoc; } function transformToHTML(xmlDoc, xslDoc) { var html = ""; if (window.XSLTProcessor) { var xsltProc = new XSLTProcessor(); xsltProc.importStylesheet(xslDoc); var fragment = xsltProc.transformToFragment(xmlDoc, document); html = new XMLSerializer().serializeToString(fragment); } else if (window.ActiveXObject) { html = xmlDoc.transformNode(xslDoc); } return html; } function getData() { //Check to see if the XMlHttpRequest object is ready and whether it has //returned a legitmate response if (xHRObject.readyState == 4 && xHRObject.status == 200) { var xml = loadDocument(“Stocks.xml”);// 装入XML var xsl = loadDocument(“Stocks.xsl”); //装入XSL document.getElementById("Stocks").innerHTML = transformToHTML(xml, xsl); //Transform setTimeout("getDocument()", 5000); // 每隔5秒钟清除对象,调用getDocument函数 } } function getDocument() { xHRObject.open("GET", "GetStocksList.php?id=" + Number(new Date()), true); xHRObject.onreadystatechange = getData; if (xHRObject.overrideMimeType) { xHRObject.overrideMimeType("text/plain"); } xHRObject.send(null); } 9-28
9.4 轮询服务模式 • 创建GetStocksList.php文件: <?php $dom = new DomDocument('1.0'); $dom->load('stocks.xml'); $root = $dom->lastChild; srand(time()); foreach ($root->childNodes As $child) { $RandomNumber = (rand()%100); if ($child->nodeType == 1) { foreach ($child->childNodes As $subchild) { if ($subchild->nodeName == "price") { $value = $subchild->textContent; $RandomPrice = (rand()%$value); $RandomPrice = $RandomPrice / 10; $direction = ""; if (($RandomNumber % 2) == 1) { $direction = "Down"; $value = $value - $RandomPrice; } else { $direction = "Up"; $value = $value + $RandomPrice; } $textNode = $dom->createTextNode($value); $subchild->nodeValue = ""; $newchild = $subchild->appendChild($textNode); } if ($subchild->nodeName == "direction") { $textNode = $dom->createTextNode($direction); $subchild->nodeValue = ""; $newchild = $subchild->appendChild($textNode); } } } } $dom->save('stocks.xml'); ?> 9-29
9.4 轮询服务模式 • 准备后台股票数据的XML文件Stocks.xml: <?xml version="1.0" encoding="iso-8859-1" standalone="yes"?> <stocks> <company> <name>Isotope International Ltd</name> <price>8.4</price> <direction>Down</direction> </company> <company> <name>Cartkeys Conglomerates</name> <price>3.2</price> <direction>Down</direction> </company> <company> <name>Merrible Networks Inc</name> <price>36.9</price> <direction>Down</direction> </company> <company> <name>Sable Strongholds</name> <price>38.4</price> <direction>Down</direction> </company> </stocks> 9-30
9.4 轮询服务模式 • 创建Stocks.xsl文件: <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" indent="yes" version="4.0"/> <xsl:template match="/"> <xsl:call-template name="DisplayStocks"></xsl:call-template> </xsl:template> <xsl:template name="DisplayStocks"> <table id="stocks"> <thead class="head"> <tr> <td>Company</td><td>Price</td><td>Direction</td> </tr> </thead> <tbody> <xsl:for-each select="//company"> <tr> <td class="border2"> <xsl:value-of select="name"/> </td> <td class="border2" align="center"> <xsl:value-of select="price"/> </td> <xsl:choose> <xsl:when test="direction='Up'"> <td class="border2" style="color:blue;"> <xsl:value-of select="direction"/> </td> </xsl:when> <xsl:otherwise> <td class="border2" style="color:red;"> <xsl:value-of select="direction"/> </td> </xsl:otherwise> </xsl:choose> </tr> </xsl:for-each> </tbody> </table> </xsl:template> </xsl:stylesheet> 9-31
9.4 轮询服务模式 • 运行:初始股价如下左图,在Stocks.xml文件中将第一支股票价格修改成8.9,然后保存,则页面自动更新后第一支股票的价格变成为8.9!见下右图: 9-32
9.5 错误处理模式 使用Ajax的问题之一是某些技术可能会导致一些异常,也可能碰见页面延迟,或者出现根本不出现呈现的问题。这种问题可能是由小的输入错误或页面的拼写错误引起。 对于这些错误,我们指望应用程序更健壮些,而不是一出现故障就停止运行。本节只讨论应用程序可以处理的一些常见情况。如果出现错误,则可能是下面3种错误之一: • 404:无法找到页面 • 302:存在页面,但要求具有重定向权限; • 200:无错误,但页面不显示。 9-33
9.5 错误处理模式 前述3种错误中,首先介绍最后一种错误,因为前述3种错误中的前2个错误则比最后一种错误容易处理些。 对于第3种错误,因为常见的解决方案对它起不了多大作用。这意味着服务器端代码或XMLHttpRequest对象调用的代码存在错误。使得responseText 或 responseXML 属性为空。产生这种错误的常因是代码中的逻辑错误。它暗示了代码已经正确执行,但无返回结果。 在这种情况下,XMLHttpRequest 对象的status属性将为200。 9-34
9.5 错误处理模式 • 问题 XMLHttpRequest对象返回为4的readyState,但返回200以外的status时该怎么做? • 解决模式 XMLHttpRequest对象经历了初始化对象、发送数据和接收响应等一系列阶段。它从0运行到4。通常,我们希望status属性返回200,readyStatus返回4。如果没有得到预期结果,则可能引起脚本崩溃。因为一旦返回值为4的readyStatus,除非重新设置,否则readyStatus是不会发生改变的。我们有两种解决模式: • 第一种模式是完全取消请求; • 第二种模式是继续在指定的时间或请求次数内执行请求。 下面来看看两种模式的实际示例。 9-35
9.5 错误处理模式 • 例1:取消请求。以本章第2节的示例为例。 我们对用户键盘输入的用户名是否包含在服务器上数据库中UserName表中,并使用XMLHttpRequest对象执行表单验证。 注意,Internet上的各种干扰使得有可能不返回status为200的响应。在这种情况下,必须采取其他措施。通过将XMLHttpRequest对象调用的文件名改为不存在的文件名,来模拟Internet上的干扰条件。 在status不为200的情况下,显示第三种替代信息。 9-36
9.5 错误处理模式 • 将index.htm中的FormValidation.js改为FormValidation2.js <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>Form Validation Example</title> <link id="Link1" rel="stylesheet" href="FormValidation.css" type="text/css" /> <script type="text/javascript" src="FormValidation2.js"></script> </head> <body> <form name="form1" id="form1" method="post" action="formcheck.php"> User Name: <input id="UserName" type="text" onblur="Validate('UserName')" /> <br/> <span id="span"></span> <br/><br /> Address:<input id="Address" class="textbox" type="text" /> <br /><br /> <input type="button" value="Click here to submit details" /> </form> </body> </html> 9-37
9.5 错误处理模式 function Validate(data) { var newform = document.forms[0]; var bodyofform = getBody(newform, data); if (bodyofform != "UserName=") { xHRObject.open(“POST”, “thisfiledoesntexist.php”, true); //不存在的文件,导致status不等于200的错误发生 xHRObject.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xHRObject.onreadystatechange = getData; xHRObject.send(bodyofform); ; } else { span.innerHTML = "Blank user names not allowed"; } } Function Change() { if (state.innerHTML == "State:") { state.innerHTML = "County:" zipcode.innerHTML = "Postcode:" } else { state.innerHTML = "State:" zipcode.innerHTML = "Zipcode:" } } // 以上是FormValidation2.js程序 var xHRObject = false; if (window.ActiveXObject) { xHRObject = new ActiveXObject("Microsoft.XMLHTTP"); } else if (window.XMLHttpRequest) { xHRObject = new XMLHttpRequest(); } function getData() { if ((xHRObject.readyState == 4)) { if (xHRObject.status == 200) { var serverText = xHRObject.responseText; if (serverText == "True") { span.innerHTML = "This user name has already been taken"; } if (serverText == "False") { span.innerHTML = "This user name is available"; } } else { span.innerHTML = "现在不能确定库中是否存在输入的名称,提交后再检验"; xHRObject.abort(); } } } function getBody(newform, data) { var argument = data + "="; argument += encodeURIComponent(document.getElementById(data).value) return argument; } 9-38
9.5 错误处理模式 • 运行程序,输入一个库中存在的姓名Peter, 则出现status不为200的错误: 9-39
9.5 错误处理模式 • 例2:多次请求。以本章第3节示例为例,修改polling.htm文件: <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>Polling The Server Example</title> <link id="Link1" rel="stylesheet" href="Polling.css" type="text/css" /> <script type="text/javascript" src="polling2.js"></script> </head> <body onload="getDocument()"> <span id="Stocks"></span> </body> </html> 9-40
9.5 错误处理模式 • 将polling.js文件修改成polling2.js文件: var xHRObject = false; if (window.XMLHttpRequest) { xHRObject = new XMLHttpRequest(); } else if (window.ActiveXObject) { xHRObject = new ActiveXObject("Microsoft.XMLHTTP"); } function loadDocument(fileName) { var xmlDoc = null if (window.ActiveXObject) { xmlDoc = new ActiveXObject("MSXML2.DOMDocument.3.0") } else { document.implementation.createDocument("","",null); } xmlDoc.async = false; xmlDoc.load(fileName); return xmlDoc; } function transformToHTML(xmlDoc, xslDoc) { var html = ""; if (window.XSLTProcessor) { var xsltProc = new XSLTProcessor(); xsltProc.importStylesheet(xslDoc); var fragment = xsltProc.transformToFragment(xmlDoc, document); html = new XMLSerializer().serializeToString(fragment); } else if (window.ActiveXObject) { html = xmlDoc.transformNode(xslDoc); } return html; } function getData() { if (xHRObject.readyState == 4) { if (xHRObject.status == 200) { var xml = loadDocument("Stocks.xml"); // Load XML var xsl = loadDocument("Stocks.xsl"); //Load XSL document.getElementById("Stocks").innerHTML = transformToHTML(xml, xsl); //Transform setTimeout("getDocument()", 5000); } else { var Stocks = document.getElementById("Stocks"); if (Stocks.innerHTML.indexOf("available")==-1) { Stocks.innerHTML += “<br/><span> 当前股票信息不可用,仅显示最近的可用信息</span>"; } xHRObject.abort(); setTimeout("getDocument()", 5000); } } } function getDocument() { xHRObject.open("GET", "GetStocksList.php?id=" + Number(new Date()), true); xHRObject.onreadystatechange = getData; if (xHRObject.overrideMimeType) { xHRObject.overrideMimeType("text/plain"); } xHRObject.send(null); } 9-41
9.5 错误处理模式 • 运行程序polling.htm,然后将Apache或IIS服务停止,在尝试4次后,出现下图所示的提示。当然,如果再次启动Web服务,则程序运行进入正常。 9-42