在主页中,用于登记新客户信息的一个简单页面如下所示:
你可能已经留意到了,onsubmit事件处理函数现在改为调用sendRequest()函数(尽管事件处理函数仍返回false以防止实际的页面提交)。这个方法首先聚集POST请求所需的数据,接着创建XMLHttp对象并发送出去。数据必须作为一个查询字符串,以如下格式的发送:<form method="post" action="SaveCustomer.php"
onsubmit="sendRequest(); return false">
<p>Enter customer information to be saved:</p>
<p>Customer Name: <input type="text" name="txtName" value="" /><br />
Address: <input type="text" name="txtAddress" value="" /><br />
City: <input type="text" name="txtCity" value="" /><br />
State: <input type="text" name="txtState" value="" /><br />
Zip Code: <input type="text" name="txtZipCode" value="" /><br />
Phone: <input type="text" name="txtPhone" value="" /><br />
E-mail: <input type="text" name="txtEmail" value="" /></p>
<p><input type="submit" value="Save Customer Info" /></p>
</form>
<div id="divStatus"></div>
name1=value1&name2=value2&name3=value3
为防止传输过程中出现数据丢失,其中每一个参数的name和value必须经过URL编码,而JavaScript提供了一个名为encodeURLComponent()的内置函数,可用于进行这种形式的编码。为创建这个字符串,还需要遍历所有的页面字段,取出name和value并进行编码,如下的getRequestBody()函数便是进行这种操作:
function getRequestBody(oForm) { var aParams = new Array(); for (var i=0 ; i < oForm.elements.length; i++){ var sParam = encodeURIComponent(oForm.elements[i].name); sParam += "="; sParam += encodeURIComponent(oForm.elements[i].value); aParams.push(sParam); } return aParams.join("&"); }
函数假定你用一个页面的引用作为参数,创建了一个数组(aParams)以保存每一对单独的name-value,接下来,遍历完页面中的元素,构建一个字符串,并把它存入sParam,而sParam将会在随后添加到数组中;这样做的目的是为了防止多个字符串之间的级联,而其将会在某些浏览器中导致代码执行缓慢。最后一步是在数组上调用join(),并传递给它一个与符号(&),通过这个与符号,有效地结合了所有的name-value,以正确的格式创建出一个单一的字符串。
在大多数浏览器中,字符串的级联是一个开销巨大的处理过程,因为字符串是不可变的,这意味着一旦创建,它们不能改变自身的值,因此,级联两个字符串将导致创建一个新的字符串,并把这两个字符串的内容复制进去,而此过程的周而复始将严重拖慢系统的速度,因为这个原因,最好少用字符串的级联操作,而使用数组的join()方法去处理较长的字符串。
以下的sendRequest()函数调用getRequestBody()并设置好请求:
function sendRequest() { var oForm = document.forms[0]; var sBody = getRequestBody(oForm); var oXmlHttp = zXmlHttp.createRequest(); oXmlHttp.open("post", oForm.action, true); oXmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); oXmlHttp.onreadystatechange = function (){ if (oXmlHttp.readyState == 4){ if (oXmlHttp.status == 200){ saveResult(oXmlHttp.responseText); } else { saveResult("An error occurred: " + oXmlHttp.statusText); } } }; oXmlHttp.send(sBody); }
如同前一个示例一样,此函数中的第一步是得到对页面的一个引用,并把它存到一个变量中(oForm);接着,生成请求体存在sBody中,并创建和设置好XMLHttp对象。注意此处open()中现在已用post代替了get,且第二个参数设置为oForm.action(正是它,所以此脚本才可用于多页面中)。你可能也注意到了,此处也设置了一个请求信息头,当一个页面从浏览器post到服务器时,由它将请求的内容类型设置为application/x-www-form-urlencoded,而大多数的服务器端语言都会查找这条编码,以正确解析传入的POST数据,所以设置它是非常重要的。
而onreadystatechange事件处理函数与GET示例中的是非常类似的,唯一的不同是调用saveResult()而不是displayCustomerInfo();上例的最后一行非常重要,当字符串sBody传递给send()时,它也将成为请求体的一部分,这实际上模仿了浏览器行为,因此服务器端逻辑也会工作正常。
XMLHttp的有利与不利之处
毫无疑问,用于“客户——服务器”通讯的XMLHttp,相比隐藏框架,优势非常明显,编写的代码更加“干净”,另外也不像隐藏框架那样使用大量的回调函数,现在,代码表现的意图清晰易懂。还可以像访问HTTP状态码一样,访问到请求和响应头部信息,让你自己来判断请求是否成功。
不利的方面是,不同于隐藏框架,现在没有函数调用的浏览器历史记录,而“前进”与“后退”按钮也不再与XMLHttp请求有关联,等于是抛弃了这两个按钮,正因为此,许多的Ajax程序都会混合使用XMLHttp与隐藏框架来创建真正实用的界面。
另一个不利方面——在此只关系到Internet Explorer,现在是必须依靠ActiveX控件,如果用户设置了一个特定的安全区域,不允许执行ActiveX控件,你就不可能访问到XMLHttp对象,如果这样,那就不得不使用隐藏框架了。
