第十五章 JavaScript與XML
1.浏覽器對XML DOM的支持
1.1 DOM2級核心
①在DOM2級在document.implementation中引入了createDocument()方法(IE6~8不支持)。
可以創建一個空白XML。
var xmldom = document.implemention.createDocument(namespaceUri,root,docype);
②要創建一個新的文檔元素為<root>的XML文檔,可用如下代碼:
var xmldom = document.implementation.createDocument("","root",null);
aert(xmldom.documentElement.tagName); //"root"
var child = xmldom.createElement("child");
xmldom.documentElement.appendChild(child);
③檢測浏覽器是否支持DOM2級XML:
var hasXmlDom = document.implementation.hasFeature("XML","2.0");
1.2 DOMParse類型
①Firefox、Opera、Chrome、Safari支持(IE8不支持),DOMParse類型可將XML解析為DOM文檔。
②創建一個DOMParse實例,再調用parseFromString()方法。這個方法接受兩個參數:要解析的XML字符串和內容類型(內容類型始終為"text/xml")。返回值是一個Document實例。
var parser = new DOMParse();
var xmldom = parser.parseFromString("<root><child/></root>","text/xml");
alert(xmldom.documentElement.tagName); //"root"
alert(xmldom.documentElement.firstChild.tagName); //"child"
var anotherChild = xmldom.createElement("child");
xmldom.documentElement.appendChild(anthorChild);
var children = xmldom.getElementsByTagName("child");
alert(children.length); //2
③發生解析錯誤時,仍然會從parseFromString()中返回一個Document對象。但這個對象的文檔元素是<parsererror>(Firefox、Opera);對象根元素第一個子元素為<parsererro>(Safari、Chrome)。
<parseerror>元素的內容是對解析錯誤地描述。通過getElementsByTagName()查找<parsererror>確定是否有解析錯誤。
var parse = new DOMParse();
var xmldom = parser.parseFromString("<root>","text/xml");
var errors = xmldom.getElementsByTagName("parsererror");
if(errors.length > 0 ){
alert("Parsing error!");
}
1.3 XMLSerializer類型
①此類可將DOM文檔序列化為XML字符串。(IE6~8不支持)。
②要序列化DOM文檔,首相必須創建XMLSerializer實例,然後將文檔傳入其serializerToString()方法:
var serializer = new XMLSerializer();
var xml = serializer.serializeToString(xmldom);
alert(xml);
1.4 DOM3級加載和保存
①DOM3級“加載和保存”規范的目的在於將加載、解析和序列化XML文檔的任務標准化,模塊規定了兩個解析方法:同步方法和異步方法。
②檢測DOM3級加載和保存
var hasLSSync = document.implementation.hasFeature("LS","3.0");
var hasLSAsync = document.implementation.hasFeature("LS-Async","3.0");
③DOM3級“加載和保存”還為document.implementation對象添加了下列新屬性和新方法:
□MODE_SYNCHRONOUS:為同步解析方式定義的常量;
□MODE_ASYNCHRONOUS:為異步解析方式定義的常量;
□createLSParse(mode,schemaType):創建一個在指定方式(mode)下運行且符合指定模式(schema)類型的解析器。
□createLSSerializer():創建一個新XML序列化器。
□createLSInput():創建一個新的輸入對象,用於解析/序列化操作。
□createLSOutput():創建一個新的輸出對象,用於解析/序列化操作。
1.4.1 解析XML
(1)同步解析器
①需要先創建同步解析器。如果解析器並不想基於哪個模式進行驗證,為createLSParser()的第二個參數傳入null。如果需要基於XML模式進行驗證,則應為第二個參數傳入"http://www.w3.org/2001/XMLSchema",如果要基於XML DTD進行驗證,則應該為第二個參數傳入"http://www.w3.org/TR/REC-xml"。
②解析之前還要創建一個新的LSInput對象。為此,要使用createLSInput()方法;創建LSInput對象後,還需要將XML字符串賦值給該對象的stringData屬性。
③解析完成後,就會返回一個XML DOM文檔對象
④如果在同步解析方式下發生解析錯誤,則會拋出錯誤。
⑤示例:
var implementation = document.implementation;
var parser = implementation.createLSParser(implementation.MODE_SYNCHRONOUS,null);
var input = implement.createLSInput();
input.stirngData = "<root>";
try{
xmldom = parser.parse(input);
}catch(ex){
alert("Parsing error!");
}
(2)異步解析器
①需要在createLSParser()的第一個參數的位置上傳入常量MODE_ASYNCHRONOUS。
②通過addEventListener()來預訂load事件,以便知道文檔何時解析完畢。
③如果異步解析期間發生錯誤,則不會觸發load事件。要捕獲這個錯誤,需要使用LSParser對象上一個名叫domConfig的特殊接口定義一個錯誤處理程序。(BUG:Opera9.5不會觸發load)
④domConfig為DOMConfiguration類型的實例,表示針對特定文檔的解析和格式化規則。LSParser會用此對象指定額外配置信息,需調用setParameter()方法。其中一個參數是"error_handler",用於指定處理解析錯誤的函數。
var implementation = document.implementation;
var parser = implementation.createLSParser(implementation.MODE_ASYNCHROUNS,null);
var input = implementation.createLSInput();
input.stringData = "<root>";
//預訂load事件,但不會觸發load事件
parser.addEventListener("load",function(event){
var xmldom = event.newDocument;
var input = event.input;
alert(xmldom.documentElement.tagName); //"root"
alert(xmldom.documentElement.firstChild.tagName); //"child"
var anotherChild = xmldom.createElement("child");
xmldom.documentElement.appendChild(anotherChild);
var children = xmldom.getElementsByTagName("child");
alert(children.length); //2
},false);
parser.domConfig.setParameter("error_handler",fucntion(ex){
alert("Parsing error!");
});
//開始解析
parser.parse(input);
1.4.2 其它解析方式
通過LSParser還可以執行兩種類型的解析:解析來自URI的文件和基於上下文解析。
(1)解析來自URI的XML。
①不用創建LSInput。
②開始解析時調用parseURI()方法,並為其傳入一個指向有效XML的URI。Parser.parseURI("example.xml");
(2)基於上下文解析
①首先解析字符串,然後將解析結果插入另一個文檔。
②使用parseWithContext()方法接受3個參數:LSInput對象、上下文節點和要執行的操作。
□LSInput對象的stringData屬性中必須包含XML片段的代碼,不能含有XML序言內容(prolog)。
□上下文節點是解析完的片段該插入的地方。
□要執行的操作必須是下列LSParser常量之一。
◇ACTION_APPEND_AS_CHILDERN:將解析結果作為子節點添加到上下文節點中。
◇ACTION_REPLACE_CHILDREN:先移除上下文節點的所有節點,然後將解析結果作為上下文節點的子節點插入。
◇ACTION_INSERT_BEFORE:將解析結果作為上下文節點的同輩節點,插入到上下文節點前面。
◇ACTION_INSERT_AFTER:將解析結果作為上下文節點的同輩節點,插入到上下文節點後面。
◇ACTION_REPLACE:用解析結果替換上下文節點。
◇在解析錯誤時,以上操作均會被取消。
var implementation = document.implement;
var parser = implementation.createLSPareser(implementation.MODE.SYNCHRONOUS,null);
var input = implementation.createLSInput();
input.stringData = "<root/>";
var xmldom = parser.parse(input);
var newInput = implementation.createLSInput();
newInput.StringData = "<child/>";
parse.parseWithContext(newInput,xmldom.documentElement,parser.ACTION_APPEND_AS_CHILDREN);
alert(xmldom.documentElement.firstChild.tagName); //"child"
1.4.3 序列化XML
①要調用document.implementation上的createLSSerialization()方法,創建一個LSSerializer對象。LSSerializer對象的主要方法是writeToString(),接受一個節點類型參數並返回該節點的XML代碼字符串表述。
②通過LSSerializer對象的domCOnfig屬性來設置適合打印輸出的XML字符串格式,即將該屬性的"format_pretty_print"參數設為true。
③序列化期間發生錯誤,錯誤會被拋出。通過將writeToStirng()放try-catch語句中,可以測試是否發生了錯誤。
var serializer = implementation.createLSSerializer();
serializer.domConfig.setParameter("format_pretty_print",true);
var xml = "";
try{
Xml = serializer.writeToString(xmldom);
}catch(ex){
Alert("Serialization error occurred");
}
alert(xml);
1.5 IE對XML的支持
①IE有6種不同的XML文檔版本可供選擇,只建議其中3種:
□MSXML2.DOMDocument:為方便腳本處理而更新的版本,建議僅在特殊情況下作後備版本使用。
□MSXML2.DOMDocument.3.0:為了在JavaScript中使用,這是最低的建議版本。
□MSXML2.DOMDocument.6.0:通過腳本能夠可靠地處理的最新版本。
□確定可用版本:
function createDocument(){
if(typeof arguments.callee.activeXString ! = "string"){
var versions = ["MSXML2.DOMDocument.6.0","MSXML2.DOMDocument.3.0","MSXML2.DOMDocument"];
for(var i = 0, len = versions.length; i < len; i++){
try{
var xmldom = new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
return xmldom;
}catch(ex){
//跳過
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}
②要解析XML字符串,先創建XML DOM文檔,然後調用其loadXML()方法。
var xmldom = createDocument();
xmldom.loadXML("<root><child/></root>");
□在新DOM文檔中填充了XML內容後,就可以像操作其他DOM文檔一樣操作它了(可以使用任何方法和屬性)
③如果解析過程中出錯,可以在parseError屬性對象中找到錯誤。包含多個保存錯誤信息的屬性。
□errorCode:錯誤類型的數值編碼;沒有發生錯誤時值為0
□filePos:文件中導致錯誤發生的位置。
□line:發生錯誤的行。
□linepos:發生錯誤的行中的字符。
□reason:對象錯誤地文本解析。
□srcText:導致錯誤的代碼。
□url:導致錯誤的文件的URL。
□parseError的valueof()方法返回errorCode的值。
if(xmldom.parseError != 0){
alert("An error occurred: \n Error Code:"+xmldom.parseError.errorCoed + "\n"
+ "line:" + xmldom.parseError.line + "\n"
+ "line.Pos" + xmldom.parseError.linepos + "\n"
+ "Reson:" + xmldom.parseError.reason
);
}
1.5.1序列化XML
IE序列化XML的能力內置在了XML DOM文檔中。每個XML DOM節點都有一個xml屬性,其中保存著表示該節點的XML字符串。alert(xmldom.xml)
1.5.2 加載XML文件
①與DOM3級功能類似,要加載的XML文檔必須與JS代碼來自同一服務器。
②加載文檔的方式也可以分為同步和異步兩種。要指定加載文檔的方式,可以設置async屬性,true表示異步(默認),false表示同步。使用load(URI)方式加載。
③異步加載XML,需為XML DOM文檔的onreadystatechange事件指定處理程序。有4個就緒狀態(ready state)
□1:DOM正在加載數據。
□2:DOM已經加載完數據。
□3:DOM已經可以使用,但某些部分可能還無法訪問。
□4:DOM已經完全可以使用。
□實際上,只需要關注狀態4。通過XML文檔的readyState屬性可得其就緒狀態。
var xmldom = createDocument();
xmldom.async = true;
xmldom.onreadystatechange = function(){
if(xmldom.readyState == 4){
if(xmldom.parseError != 0){
Alert("An error occurred");
}else{
//其他xmldom操作
}
}
}
xmldom.load("exmaple.xml");
注:
□onreadystatechange事件指定處理語句須在調用load()方法之前。
□在事件處理內部由於ActiveX控件為預防錯誤,不允許使用this。
1.6 跨浏覽器處理XML
function parseXML(xml){
var xmldom = null;
if(typeof DOMParser != "undefined"){
xmldom = (new DOMParser()).parseFromString(xml,"text/xml");
var errors = xmldom.getElementsByTagName("parsererror");
if(errors.length){
throw new Error("XML parsing error:" + errors[0].textContent);
}
}else if(document.implementation.hasFeature("LS","3.0")){
var implementation = document.implementation;
var parser = implementation.createLSParser(implementation.MODE_SYNCHRONOUNS,null);
var input = implementation.createLSInput();
input.stringData = xml;
xmldom = parser.parse(input);
}else if(typeof ActiveXObject != "undefined"){
xmldom = createDocument();
xmldom.loadXML(xml);
if(xmldom.parseError != 0){
throw new Error("XML parsing error:" + xmldom.parseError.reason);
}
}else{
throw new Error("No XML parser available.");
}
return xmldom;
}
用此函數解析XML字符串時,應該放在try-catch語句中
var xmldom = null;
try{
xmldom = parseXml("<root><child/><root>");
}
catch(ex){
alert(ex.message);
}
■序列化XML兼容代碼
function serializeXml(xmldom){
if(typeof XMLSerializer != "undefined"){
return(new XMLSerializer()).serializeToString(xmldom);
}else if(document.implementation.hasFeature("LS","3.0")){
var implementation = document.implementation;
var serializer = implemetation.createLSSerializer();
return serializer.writeToString(xmldom);
}else if(typeof xmldom.xml != "undefined"){
return xmldom.xml;
}else{
throw new Error("Could not serialize XMLDOM.");
}
}
2.浏覽器對XPath的支持
2.1 DOM3級Xpath
①DOM3級支持XPath,檢測浏覽器支持:
Var supportsXPath = document.implementation.hasFeature("XPath","3.0");
②DOM3級XPath中最重要的兩個類型:XPathEvaluator和XPathResult。
③XPathEvalutor用於特定的上下文中隊XPath表達式求值。有3個方法:
□createExpression(expression,nsresolver):將XPath表達式及相應的命名空間信息轉換成一個XPathExpression,這是查詢的編譯版。在多次使用一個查詢時很有用。
□createNSResolver(node):根據node的命名空間信息創建一個新的XPathNSResolver對象。在基於使用命名空間的XML文檔求值時,需要使用XPathNSResolver對象。
□evaluate(expression,context,nsresolver,type,result):在給定的上下文中,基於特定的命名空間信息來對XPath表達式求值。剩下的參數表示如何返回結果。
□在支持DOM3級的浏覽器中,Document類型通常都是與XPathEvaluator接口一起實現的。
④上面3個方法中,evalute最常用。接受5個參數:
□XPath表達式
□上下文節點
□命名空間解析器(只在XML代碼中使用了XML命名空間時有必要指定,否則為null)
□返回結果的類型(見書P415)
□保存結果的XPathResult對象(通常是null,因為結果也會以函數值形式返回)
■返回結果類型為以下的其中一個:
◇XPathResult.ANY_TYPE
◇XPathResult.NUMBER_TYPE
◇XPathResult.STRING_TYPE
◇XPathResult.BOULEAN_TYPE
◇XPathResult.UNORDER_NODE_ITERATOR_TYPE
◇XPathResult.ORDERED_NODE_SNAPSHOT_TYPE
◇XPathResult.ANY_UNORDERED_NODE_TYPE
◇XPathResult.FIRST_ORDERED_NODE_TYPE
1)指定的是迭代器結果,須用iterateNext()方法從節點中取得匹配節點,無則返回null。
var result = xmldom.evalute("employee/name",xmldom.document,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null);
if(result !== null){
var node = result.iterateNext();
while(node){
alert(node.tagName);
node = node.iterateNext();
}
}
2)指定的是快照類型,就必須使用snapshotItem()和snapshotLength屬性。
1.單節點結果
指定常量XPathResult.FIRST_ORDER_NODE.TYPE會返回第一個匹配節點,可通過singleNodeValue屬性來訪問該節點。
2.簡單類型結果
booleanValue、numberValue和stringValue
3.默認類型結果
4.命名空間支持
□對於利用了命名空間的XML文檔,XPathEvaluator必須知道命名信息,然後才能正確地進行求值。
□第一種方法是通過createNSResolver()來創建XPathNSResolver對象,這個方法接受一個參數,即文檔中包含命名空間定義的節點。
Var nsresolver = xmldom.createNSResolver(xmldom.documentElement);
Var result = xmldom.evaluate("wrox:book/wrox:author",xmldom.document,nsresolver,XPathResult.ORDERED_NODESNAPSHOTTYPE,null);
Alert(result.snapshotlength);
□第二種方法是定義一個函數,讓它接受一個命名空間前綴,返回關聯的URI。
Var nsresolver = function(prefix){
Switch(prefix){
Case "wrox" : return "http://www.wrox.com/";
//其他前綴
}
};
Var result = xmldom.evaluate("count(wrox:book/wrox:author)",xmldom.document,nsresolver,XPathResult.NUMBER_TYPE,null);
2.2 IE中的XPath
①IE對XPath的支持是內置在XML DOM文檔對象中的。含有兩個方法。
□selectSingleNode()方法接受一個XPath模式,在找到匹配節點時返回第一個匹配的節點。
var element = xmldom.documentElement.selectSingleNode("employee/name");
□selectNodes(),接受一個XPath模式作參數,返回與模式匹配的所有節點的NodeList(如果沒有匹配的節點,則返回一個包含零項的NoedList)。
Var elements = xmldom.documentElement.selectNodes("employee/name");
Alert(elements.length);
②IE對命名空間的支持
□必須知道自己使用的命名空間,並按格式創建一個字符串:
"xmlns:prefix1 = 'uri1' xmlns:prefixf2 = 'uri2' xmlns:prefix3:'uri3' "
□將上述格式字符串傳入到XML DOM文檔對象的特殊方法setProperty()中。
□setProperty()接受兩個參數:要設置的屬性名和屬性值。這裡屬性名應為"SelectionNameSpaces",屬性值是前面格式的字符串。
xmldom.setProperty("selectionNameSpaces","xmlns:wrox = http://www.wrox.com");
var result = xmldom.documentElement.selectNodes("wrox:book/wrox:author");
2.3 跨浏覽器使用XPath
□selectSingleNode()函數兼容代碼
function selectSingleNode(context,expression,namespace){
var doc = (context.nodeType != 9 ? context.ownerDocument : context);
if(typeof doc.evaluate != "undefined"){
var nsresolver = null;
if(namespace instanceof Object){
nsresolver = fucntion(prefix){
return namespaces[prefix];
};
}
var result = doc.evaluate(expression,context,nsresolver,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
return(result !== null ? Result.singleNodeValue : null);
}else if(typeof context.selectSingleNode != "undefined"){
//創建命名空間字符串
if(namespaces instanceof Object){
var ns = "";
for(var prefix in namespaces){
if(namespaces.hasOwnproperty(prefix)){
ns += "xmlns:" + prefix + "='" + namespaces[prefix] + "' ";
}
}
doc.setProperty("SelectionNamespaces",ns);
}
return context.selectSingleNode(expression);
}else{
throw new Error("No Xpath engine found.");
}
}
□selectNodes()函數兼容代碼
P420略
3.函數對XSLT的支持
XSLT是與XML相關的一種技術,它利用XPath將文檔從一種表現形式轉換成另一種表現形式。
3.1 IE中的XSLT轉換
1.簡單的XSLT轉換
□最簡單方法:XSLT、XML分別加載到一個DOM文檔中,再使用transformNode()方法。
□transformNode()方法:只有一個參數——包含XSLT樣式表的文檔。返回一個包含轉換信息的字符串。
□可在xmldom各個節點機型轉換。
//加載xmldom各個節點進行轉換
xmldom.load("employees.xml");
xsltdom.load("employees.xslt");
//轉換
var result = xmldom.transformNode(xsltdom);
2.復雜的XSLT轉換
①基本原理:
a.將xml加載到“線程安全的XML DOM”中。
b.將XSLT加載到“XSL模板”中
c.使用“XSL處理器”進行轉換。
□自由線程DOM:MSXML2.FreeThreadedDOMDocument
□XSL模板:MSXML2.XSLTemplate
□XSL處理器:調用XSL模板的createProcessor()創建XSL處理器。
②把XSLT樣式表加載到一個線程安全的XML文檔中。
function createThreadSafeDocument(){
if(typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.FreeThreadedDOMDocument.6.0","MSXML2.FreeThreadedDOMDocument.3.0","MSXML2.FreeThreadedDOMDocument\"];
for(var i=0,len = version.length; i<len; i++){
try{
var xmldom = new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
return xmldom;
}catch(ex){
// 跳過
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}
□線程安全的XML DOM與常規XML DOM使用方式一致。
var xsltdom = createThreadSafeDocument();
xsltdom.async = fasle;
xsltdom.load("employee.xslt");
③為自由線程DOM指定XSL模板
fucntion createXSLTemplate(){
if(typeof arugments.callee.activeXString != "string"){
var versions = ["MSXML2.XSLTemplate.6.0","MSXML2.XSLTemplate.3.0","MSXML2.XSLTemplate"];
for(var i=0,len=versions.length;i<len;i++){
try{
var template = new ActiveXObject(versions[i]);
arguments.callee.activeXString = version[i];
return template;
}catch(ex){
//跳過
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}
□使用createXSLTemplate()函數創建用法:
var template = createXSLTemplate();
template.stylesheet = xsltdom;
var processor = template.createProcessor();
processor.input = xmldom;
processor.transform();
var result = processor.output;
④創建XSL處理器之後,須將要轉換的節點指定給input屬性。然後,調用transform()方法執行轉換並將結果左字符串保存在output屬性中。
⑤使用XSL處理器可以對轉換進行更多的控制,同時也支持更高級的XSLT特性。
□addParameter():兩個參數:要設置的參數名稱(與在<xsl:param>的name特性中給指定的一樣)和要指定的值(多數是字符串,也可以是數值或布爾值)。
□setStartMode():接受一個參數,即要為處理器設置的模式。
□reset():重置處理器,清除原先的輸入和輸出屬性、啟動模式及其它指定的參數。processor.reset();
3.2 XSLTProcessor類型(非IE浏覽器支持)
①基本原理:
a.加載兩個DOM文檔,一個基於XML,另一個基於XSLT。
b.創建一個新XSLTProcessor對象
c.使用importStylesheet()方法指定一個XSLT
d.最後使用transformToDocument()或transfromToFragment()方法可得一個文檔片段對象。
var processor = new XSLTProcessor();
processor.importStylesheet(xsltdom);
□transformToDocument():只要傳入XML DOM,就可將結果作為一個完全不同的DOM文檔使用。
□transformToFragement():兩個參數:要轉換的XML DOM和應該擁有結果片段的文檔
var fragment = processor.transformToDocument(xmldom,document);
②使用參數:
□setParameter():3個參數:命名空間URI(一般為null)、參數內部名稱和要設置的值
□getParameter():取得當前參數的值,兩個參數:命名空間和參數內部名。
□removeParameter():移除當前參數的值,兩個參數:命名空間和參數內部名。
③重置處理器
□reset()方法:從處理器中移除所有參數和樣式表。
3.3 跨浏覽器使用XSLT
IE對XSLT轉換支持與XSLTProcessor區別太大,跨浏覽器兼容性最好的XSLT轉換技術,只能是返回結果字符串。
function transform(context,xslt){
if(typeof XSLTProcess != "undefined"){
var processor = new XSLTProcessor();
processor.importStylesheet(xslt);
var result = processor.transfromToDocument(context);
return(new XMLSerialize()).serializeToString(result);
}else if(typeof context.transforamNode != "undefined"){
return context.transformNode(xslt);
}else{
throw new Error("No XSLT processor available.");
}
}
第十六章 E4X (略)
第十七章 Ajax與JSON
1.XHR對象
①IE6需要使用MSXML庫中的一個ActiveX對象實現,而其他浏覽器原生支持XHR對象。
function createXHR(){
if(typeof XMLHttpRequest != "undefined"){
return new XMLHttpRequest();
}else if(typeof ActiveXobject != "undefined"){
if(typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"];
for(var i=0, len = versions.length; I <len; i++){
try{
var xhr = new ActiveXObject(versions[i]);
Arguments.callee.activeXString = versions[i];
return xhr;
}catch(ex){
//跳過
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}else{
throw new Error("No XHR object available");
}
}
1.1 XHR的用法
①open()方法
□接受3個參數:要發送的請求的類型(“get”、“post”等)、請求的URL和表示是異步發送請求的布爾值。
□參數URL是相對於執行代碼的當前頁面(當然也可以使用絕對路徑);
□調用open()方法並不會真正發送請求,而只是啟動一個請求以備發送。
②send()方法
□要發送特定的請求,必須使用send()方法啟動。
□接受一個參數,即要作為請求主體發送的數據。如果不需要通過請求主體發送數據,則必須傳入null,因為這個參數對有些浏覽器來說是必須的。
□調用send()之後,請求就會被分派到服務器。
③相應的數據會自動填充XHR對象的屬性,相關的屬性簡介如下:
□responseText:作為相應主體被返回的文本。
□responseXML:如果響應的內容是“text/xml”或“application/xml”,這個屬性中將保存含著響應數據的XML DOM文檔。
□status:響應的HTTP狀態。
□statusText:HTTP狀態的說明。
④處理響應的判斷
□檢查status屬性,以確定響應成功返回。
□HTTP狀態代碼為200是成功的標志,此時responseText、responseXML應能訪問。
□HTTP狀態代碼為304表示請求的資源沒修改,可使用緩存值。
⑤同步請求
xhr.open("get", "example.txt", false);
xhr.send(null);
if( (xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.statusText);
}else{
alert("Request was unsuccessful:" + xhr.status);
}
⑥異步請求
1)發送異步請求還需要檢測XHR對象的readyState屬性,該屬性表示請求/響應過程的當前活動階段。取值如下:
□0:未初始化。尚未調用open()方法。
□1:啟動。已調用open()方法,未調用send()方法。
□2:發送。已調用send()方法,但尚未接收到響應。
□3:接收。已接收到部分響應數據。
□4:完成。已經接收到全部響應數據,而且已經可以在客戶端使用了。
2)readyState值的變化會觸發readyStatechange事件。由於並非所有浏覽器支持DOM2級方法,因此用DOM0級添加處理程序。
var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if( (xhr.staus >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
}else{
alert("Request was unsuccessful: " + xhr.status);
}
}
};
xhr.open("get","example.txt", true);
xhr.send(null);
⑦在接收到響應之前還可以調用abort()方法取消異步請求。xhr.abort();
1.2 HTTP頭部信息
XHR對象提供了操作“請求頭部”和“響應頭部”信息的方法。
①請求頭部
1)請求頭部信息
□Accept:浏覽器能夠處理的內容類型。
□Accept-Charset:浏覽器能夠顯示的字符集。
□Accept-Encoding:浏覽器能夠處理的壓縮編碼。
□connection:浏覽器與服務器之間連接的類型。
□Cookie:當前頁面設置的任何cookie。
□Host:發出請求的頁面所在的域。
□Referer:發出請求的頁面的URI。
□User-Agent:浏覽器的用戶代理字符串。
②setRequestHeader()方法可以設置自定義的請求頭部信息。
□接收兩個參數:頭部字段名稱和頭部字段的值。
□要成功發送請求頭部信息,必須在調用open()方法之後且調用send()方法之前。
③getResponseHeader()方法,傳入頭部字段名稱,可以取得相應的響應頭部信息。
④getAllResponseHeader()方法,取得一個包含所有頭部信息的長字符串。
1.3 GET請求
①常用語服務器查詢信息。
②GET請求經常會發生查詢字符串格式問題。查詢字符串中每個參數的名稱和值都必須使用encodeURIComponent()進行編碼。
③輔助向現有URL末尾添加查詢字符串參數:
function addURLParam(url, name, value){
url += (url.indexOf("?") == -1 ? "?" : "&" );
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}
1.4 POST請求
①通常用於向服務器發送應該被保存的數據。
②xhr.open("post","example.php",true);發送post請求的第二步就是向send方法中傳入某些數據。
1.5浏覽器差異
①IE
□IE為XHR對象添加了一個timeout屬性,表示請求在等待響應。
□規定時間內沒有接收到響應,就出發timeout事件,進而調用ontimeout事件處理程序。
②Firefox
1)load事件
2)progress事件
2.跨域請求
①IE中XDomainRequest對象
1)XDR與XHR區別
□cookie不會隨請求發送,也不會隨響應返回。
□只能設置請求頭部信息中的Content-Type字段。
□不能訪問響應頭部信息。
□只支持GET和POST請求。
□XDR只能訪問Access-Control-Allow-Origin頭部設置為有當前域的資源。
2)所有XDR請求都是異步執行的,不能創建同步請求。
□返回請求之後,會觸發load事件,響應的數據也會保存在response屬性中。
□接收到響應後,只能訪問響應的原始文本,沒有辦法確定響應的狀態代碼。
□響應有效會觸發load事件,如果失敗(包括響應中缺少Access-Control-Origin頭部)就會觸發error事件。
□XDR也支持timeout屬性以及ontimeout事件處理程序。
□為了支持POST請求,XDR對象提供了ontentType屬性,用來表示發送數據的格式。
□在請求返回前調用abort()方法可終止請求。
var xdr = new XDomainRequest();
xdr.onload = function(){
alert(xdr.responseText);
};
xdr.timeout = 1000;
xdr.ontimeout = function(){
alert("Request took too long.");
};
xdr.open("get","http://www.xx.com/page/");
xdr.send(null);
②Firefox
1)要求遠程資源有權決定自身是否可以被遠程浏覽器訪問。
□這需要通過設置Access-Contro-Allow-Origin頭部實現。
□要訪問另一個域資源,可以使用標准XHR對象,並為open()方法傳入一個絕對URL。
2)與IE中XDR對象不同,跨域XHR對象允許訪問status和statusText屬性,也支持同步請求。
3)跨域XHR的額外限制如下:
□不能使用setRequestHeader()設置自定義頭部。
□不會發送也不會接受cookie
□getAllRequestponseHeaders()方法只能返回空字符串。
3.JSON
①JSON是純文本,而不是JavaScript代碼。JSON的設計意圖在服務器端構建格式化的數據,然後再將數據發送給浏覽器。
②由於JSON在JavaScript中相當於對象和數組,因此JSON字符串可以傳遞給eval()函數,讓其解析並返回一個對象或數組的實例。
③如果你是自己編寫代碼來對JSON求值,最好將輸入文本放在一對圓括號中。因為eval()在對輸入的文本求值時,是將其作為JS代碼而非數據格式看待。在對以左花括號開頭的對象求值時,就好像是遇到一個沒有名字的JavaScript語句,會導致錯誤。將文本放在一對圓括號中可以解決這個問題,因圓括號表示值而不是語句。
var object1 = eval("{}"); //拋出錯誤
var object2 = eval("({})"); //沒有問題
var object3 = eval("(" + jsonText + ")"); //通用解決方案
3.1在Ajax中使用JSON
①Douglas Crockford的JSON序列化器/解析器。www.json.org/js.html
②在上述庫中,有一個全局JSON對象,有兩個方法:parse()和stringify()。
③parse()方法:
□兩個參數:JSON文本和一個可選的過濾函數。在傳入的文本是有效地JSON情況下,parse()方法返回傳入數據的一個對象表示。
□例子:var object = JSON.parse("{}"); 與直接使用eval()不同的是,這裡不需要傳入的文本家圓括號(內部自動處理)。
□第二個參數是一個函數,這個函數以一個JSON鍵和值作為參數。要想讓作為參數的鍵出現在結果對象中,該函數必須返回一個值。
var jsonText = "{\"name\":\"Nicholas\",\"age\":29,\"author\":true}";
var object = JSON.parse(jsonText,function(key,value){
switch(key){
case "age" : return value+1;
case "author" : return undefined;
default : return value;
}
});
alert(object.age); //30
alert(object.author); //undefined
④JSON同樣也是向服務器發數據的流行格式。發送數據時,一般會把JSON放到POST請求主體中,而JSON對象的stringify()方法正是為此而設計。
⑤stringify()方法:
□三個參數:要序列化的對象、可選的替換函數(用於替換未受支持的JSON值)和可選的縮進說明符(可以是每個級別縮進的空格數,也可以是用來縮進的字符)。
var contact = {
name : "Nicholas C. Zakas",
email : "nicholas@some-domain-name.com"
};
var jsonText = JSON.stringify(contact);
alert(jsonText);
□JSON序列化支持的類型:字符串、數值、布爾值、null、對象、數組和Date(Date將被換成字符串形式)。其他不支持類型將被移除,可通過stringify()第二個參數所傳入的函數改變行為。
3.2安全
①JSON缺點:使用eval(),有可能受到XSS攻擊。
②建議使用Crockford的庫,可妥當解析JSON字符串,過濾其中惡意代碼。降低遭受代碼式XSS攻擊的可能性。