XML、XMLDOM、XMLHttpRequest 详解(一)

XMLDOM、XMLHttpRequest 是 JavaScript 中比较核心比较重要的一些点,现在有很多成熟的库帮我们封装了这些功能,包括兼容性方便也做了处理,很方便我们使用,但是这些底层核心,我觉得还是很有必要学习了解一下。

背景知识

在介绍这些知识点之前,很有必要先来了解一下 DOM(Document Object Model) 的历史发展过程,以及它的 level 。

DOM 可被 JavaScript 用来读取、改变 HTML、XHTML 以及 XML 文档。
DOM 被分为不同的部分(核心、XML及HTML)和级别(DOM Level 1/2/3/4)

  • Netscape Navigator 4和IE4分别发布于1997年的6月和10月发布的DHTML,他们是未形成标准的试验性质的初级阶段的DOM,称为DOM0,并不是标准。

  • 在 1998 年,W3C 发布了第一级的 DOM 规范(DOM1)。这个规范允许访问和操作 HTML 页面中的每一个单独的元素。

  • DOM2 在 2000年发布

  • DOM3 在 2004年发布

  • DOM4 在 2015 年发布

(以上这些内容可以在 w3c 的官网查到,https://www.w3.org/TR/?tag=dom ,现在能查到的最早的记录是 2000年的 DOM2)

与此同时,也能看到现在最新的 DOM 级别是 DOM4。(于 2015-11-19 发布)
DOM4
最新的版本是 DOM4.1 。
DOM4.1

OK,那么,现在需要知道的是:1、DOM标准肯定是按照更标准,更好,更完美的方向前进的。2、不同的浏览器实现的标准不一样(有的是按照DOM3的标准来实现的,有的是按照DOM2的标准来实现的,按照DOM1标准来实现的也有,最新的浏览器应该也有按照DOM4的标准来做的吧)。3、DOM3的标准持续时间最长,虽然现在 DOM4 发布了,但是现在主流的标准还是 DOM3,所以现在大部分浏览器都是实现了 DOM3 的标准。

正因为不同的浏览器实现的标准不一样,所以我们要做兼容,这也是我们要做兼容的根本原因所在。


OK,有了这个背景知识,开始我们下面的内容


XML、XMLDOM

在之前的IE6、7、8,微软为了开发人员方便的处理 XML,创建了 MSXML 库。

var xmlDom = new ActiveXObject('MSXML2.DOMDocument');

下面有一份 ActiveXObject 类型表:

XML版本字符串 说明
Microsoft.XmlDom 最初随同IE发布,不建议使用
MSXML2.DOMDocument 脚本处理而更新的版本,仅在特殊情况作为备份用
MSXML2.DOMDocument.3.0 在JavaScript中使用,这是最低的建议版本
MSXML2.DOMDocument.4.0 脚本处理时并不可靠,使用这个版本导致安全警告
MSXML2.DOMDocument.5.0 脚本处理时并不可靠,使用这个版本导致安全警告
MSXML2.DOMDocument.6.0 脚本能够可靠处理的最新版本

ps:在这六个版本中微软只推荐三种:
1、MSXML2.DOMDocument.6.0 【最可靠最新的版本】
2、MSXML2.DOMDocument.3.0 【兼容性较好的版本】
3、MSXML2.DOMDocument 【仅针对IE5.5之前的版本】

考虑到这三个版本在不同的 windows 平台和浏览器下会有不同的支持,那么为了实现兼容,我们应该考虑这样操作:从6.0 -> 3.0 -> 备用版本 这条路线进行实现。
(其实,我们在做其他兼容的时候也是,从高往低来实现兼容)

function createXMLDOM() {
  var version = [
    'MSXML2.DOMDocument.6.0',
    'MSXML2.DOMDocument.3.0',
    'MSXML2.DOMDocument'
  ];
  for(var i=0; i<version.length; i++) {
    try{
      var xmlDom = new ActiveXObject(version[i]);
      return xmlDom;
    } catch(e) {
      // 跳过
    }
  }
  throw new Error('您的系统或浏览器不支持MSXML!'); // 循环后抛出错误
}

OK,我们现在通过这个方法就可以拿到一个 xmlDom 对象了。

var xmlDom = createXMLDOM();
// 载入 XML 文件
// 1、加载XML字符串loadXML()
xmlDom.loadXML('<root version="1.0"><user>Yu</user></root>');
alert(xmlDom.xml);

// 2、加载XML外部文件load()
xmlDom.load('text.xml');
console.log(xmlDom.xml)

XML 和 XHTML 一样,都是通过 DOM 节点操作的。

var user = xmlDom.getElementsByTagName('user')[0]
console.log(user.nodeType)
console.log(user.tagName)
console.log(user.firstChild.nodeValue)

DOM 不单单可以获取 XML 节点,也可以创建:

var email = xmlDom.createElement(email)
xmlDom.documentElement.appendChild(email)
// 为创建的节点添加内容
var emailText = xmlDom.createTextNode('react.dong.yu@gmail.com')
email.appendChild(emailText)

同步及异步

load() 方法是用于从服务器端载入 XML 的,并且限制在同一台服务器上的 XML 文件。那么在载入的时候有两种模式:同步和异步。

  • 同步:就是在加载 XML 完成之前,代码不会继续执行,直到完全加载了 XML 再返回。好处就是简单、方便,坏处就是如果加载的数据停止响应或延迟太久,浏览器会一直堵塞从而造成假死状态。
xmlDom.async = false    //设置同步,false
  • 异步:就是在加载 XML 时,JavaScript 会把任务丢给浏览器内部后台处理,不会造成堵塞,但要配合 readystatechange 事件使用,所以,通常我们都使用异步方法。
xmlDom.async = true    //设置异步,默认

通过异步加载,我们发现获取不到 XML 信息。原因是,它并没有完全加载 XML 就返回了,也就是说,在浏览器内部加载一点,返回一点,加载一点,返回一点。这个时候,我们需要判断是否完全加载,并且可以使用了,在进行获取输出。

XML DOM 中 readystatechange 事件

就绪状态 说明
1 DOM正在加载
2 DOM已经加载完数据
3 DOM已经可以使用,但某些部分还无法访问
4 DOM已经完全可以使用
var xmlDom = createXMLDOM()
xmlDom.async = true
xmlDom.onreadystatechange = function() {
  if(xmlDom.readyState === 4) {
    alert(xmlDom.xml)   
  }
}
xmlDom.load('test.xml')  // 放在后面重点体现异步的作用

1、可以通过 readyState 来了解事件的执行次数,将 load() 方法放到最后不会因为代码的顺序而导致没有加载。并且 load() 方法必须放在 onreadystatechange 之后,才能保证就绪状态变化时调用该事件处理程序,因为要先触发。
2、不能够使用this,不能够使用 IE 的事件处理函数,原因是 ActiveX 控件为了预防安全性问题

DOM2 中的 XML

IE可以实现了对XML字符串或XML文件的读取,其他浏览器也各自实现了对XML处理功能。DOM2级在 document.implementaion 中引入了 createDocument() 方法。其他浏览器都支持。

1、创建XMLDOM对象

var xmlDom = document.implementation.createDocument('','root',null);  // 创建
var user = xmlDom.createElement('user');  // 创建user元素
xmlDom.getElementsByTagName('root')[0].appendChild(user);  // 添加到root下
var value = xmlDom.createTextNode('Yu');  // 创建文本
xmlDom.getElementsByTagName('user')[0].appendChild(value);  // 添加到user下
alert(xmlDom.getElementsByTagName('root')[0].tagName);
alert(xmlDom.getElementsByTagName('user')[0].tagName);
alert(xmlDom.getElementsByTagName('user')[0].firstChild.nodeValue);

1、DOM2中不支持 loadXML() 方法,所以,无法简易的直接创建 XML 字符串,所以,只能采用以上的做法。
2、createDocument() 方法需要传递三个参数,命名空间,根标签名和文档声明,由于 JavaScript 管理命名空间比较困难,所以留空即可。文档声明一般根本用不到,直接null即可。命名空间和文档声明留空,表示创建XMLDOM对象不需要命名空间和文档声明。

2、DOMParser 类型
由于 DOM2 没有 loadXML() 方法直接解析 XML 字符串,所以提供了 DOMParser 类型来创建 XML DOM 对象。IE9、Safari、Chrome 和 Opera 都支持这个类型。

var xmlParser = new DOMParser();  // 创建DOMParser对象
var xmlStr = '<root><user>Yu</user></root>';  // 创建DOMParser对象
var xmlDom = xmlParser.parseFromString(xmlStr, 'text/xml');  // 创建XML DOM对象
alert(xmlDom.getElementsByTagName('user')[0].tagName)

XML DOM 对象是通过DOMParser对象中的parseFromString方法来创建的,两个参数:XML字符串和内容类型text/xml。

3、XML Serializer 类型
由于DOM2没有序列化XML的属性,所以提供了XMLSerializer类型来帮助序列化XML字符串。IE9、Safari、Chrome和Opera都支持这个类型。

var serializer = new XMLSerializer();  // 创建XMLSerializer对象
var xml = serializer.serializeToString(xmlDom);  // 序列化XML
alert(xml);

4、解析错误
在 DOM2 级处理 XML 发生错误时,并没有提供特有的对象来捕获错误,而是直接生成另一个错误的XML文档,通过这个文档可以获取错误信息。

var errors = xmlDom.getElementsByTagName('parsererror');
if (errors.length > 0) {
    throw new Error('XML格式有误:' + errors[0].textContent);
}

PS:errors[0].firstChild.nodeValue也可以使用errors[0].textContent来代替。

5、跨浏览器处理XML
如果要实现跨浏览器就要思考几个问题:
1、load()只有IE、Firefox、Opera支持,所以无法跨浏览器;
2、获取XMLDOM对象顺序问题,先判断先进的DOM3,然后再去判断落后的IE;
3、针对不同的IE和DOM2级要使用不同的序列化;
4、针对不同的报错进行不同的报错机制。

//首先,我们需要跨浏览器获取XML DOM
function getXMLDOM(xmlStr) {
  var xmlDom = null;
  if (typeof window.DOMParser != 'undefined') {  // W3C
    xmlDom = (new DOMParser()).parseFromString(xmlStr, 'text/xml');
    var errors = xmlDom.getElementsByTagName('parsererror');
    if (errors.length > 0) {
      throw new Error('XML解析错误:' + errors[0].firstChild.nodeValue);
    }
  } else if (typeof window.ActiveXObject != 'undefined') {  // IE
    var version = [
      'MSXML2.DOMDocument.6.0',
      'MSXML2.DOMDocument.3.0',
      'MSXML2.DOMDocument'
    ];
    for (var i = 0; i < version.length; i ++) {
        try {
          xmlDom = new ActiveXObject(version[i]);
        } catch (e) {
          //跳过
        }
    }
    xmlDom.loadXML(xmlStr);
    if (xmlDom.parseError != 0) {
      throw new Error('XML解析错误:' + xmlDom.parseError.reason);
    }
  } else {
    throw new Error('您所使用的系统或浏览器不支持XML DOM!');
  }
  return xmlDom;
}

// 其次,我们还必须跨浏览器序列化XML
function serializeXML(xmlDom) {
  var xml = '';
  if (typeof XMLSerializer != 'undefined') {
    xml = (new XMLSerializer()).serializeToString(xmlDom);
  } else if (typeof xmlDom.xml != 'undefined') {
    xml = xmlDom.xml;
  } else {
    throw new Error('无法解析XML!');
  }
  return xml;
}

由于兼容性序列化过程有一定的差异,可能返回的结果字符串可能会有一些不同。之于load()加载XML文件则因为只有部分浏览器支持而无法跨浏览器。

XPath

XPath 是一种节点查找手段,对比之前使用标准DOM去查找XML中的节点方式,大大降低了查找难度,方便开发者使用。但是,DOM3级以前的标准并没有就XPath做出规范;直到DOM3才首次推荐到标准规范行列。大部分浏览器实现了这个标准,IE则以自己的方式实现了XPath。

IE中的 XPath

selectSingleNode() // 获取单一节点
selectNode() // 获取节点集合
selectSingleNode() 方法接受一个 XPath 模式(也就是查找路径)找到匹配的第一个节点并将它返回,没有则返回null。

XPath 查找手段,就是路径查找,结构树查找

var node = xmlDom.selectSingleNode('root/user')
alert(node.xml)
alert(node.firstChild.nodeValue)

上下文节点: 我们通过 xmlDom 这个对象实例调用犯法,而xmlDom这个对象实例其实就是一个上下文节点,这个节点指针指向的是根,也就是root元素之前。那么如果我们把这个指针指向user元素之前,那么结果就会有所变化。

// 通过xmlDom,并且使用root/user的路径
var user = xmlDom.selectSingleNode('root/user')
alert(user.tagName)  // user

// 通过xmlDom.documentElement,并且使用user路径,省去了root
var user = xmlDom.documentElement.selectSingleNode('user')
alert(user.tagName)  // user

// 通过xmlDom,并且使用user路径,省去了root
var user = xmlDom.selectSingleNode('user')
alert(user.tagName)  // 找不到,出错

xmlDom 和 xmlDom.documentElement 都是上下文节点,主要就是定位当前路径查找的指针,而 xmlDom 对象实例的指针就是在最根上。

// 双斜杠可以获取不关心层次的第一个user
var node = xmlDom.selectSingleNode('//user[1]')
alert(node.xml)

// 通过唯一的属性找到user节点
var node = xmlDom.selectSingleNode('root/user[@id=6]')
alert(node.xml)

W3C 下的 XPath

在 DOM3 级XPath规范定义的类型中,最重要的两个类型是 XPathEvaluator 和 XPathResult。其中,XPathEvaluator 用于在特定上下文对 XPath 表达式求值。

XPathEvaluator的方法
createExpression(e,n)将XPath表达式及命名空间转化成XPathExpression
createNSResolve(n) 根据 n 命名空间创建一个新的XPathNSResolver对象
evaluate(e,c,n,t,r)结合上下文来获取XPath表达式的值

W3C实现XPath查询节点比IE来的复杂,首先第一步就是需要得到XPathResult对象的实例。得到这个对象的实例有两种方法:
一种是通过创建XPathEvaluator对象执行evaluate()方法
另一种是直接通过上下文节点对象(比如xmlDom)来执行evaluate()方法。

//两种方式创建XPathResult

var eva = new XPathEvaluator();
var result = eva.evaluate("root/user",xmlDom,null,XPathResult.ORDERED_NONE_ITERATOR_TYPE,null);
alert(result);

//使用上下文节点对象(xmlDom)创建XPathResult
var result = xmlDom.evaluate("root/user",xmlDom,null,XPathResult.ORDERED_NONE_ITERATOR_TYPE,null);
alert(result);

相对而言,第二种简单方便一点,但evaluate方法有五个属性:1·XPaht路径、2·上下文节点对象、3·命名空间求解器(通常是null)、4·返回结果类型、5·保存结果的XPathResult对象(通常是null)。

对于第四个参数:返回结果类型,有10种不同的类型,主要掌握两个:
1·获取一个单一节点。(XPathResult.FIRST_ORDERED_NODE_TYPE)(返回只包含一个节点的节点集合,且这个节点是在文档中第一个匹配的节点)
2·获取一个节点集合。(XPathResult.ORDERED_NODE_ITERATOR_TYPE)(返回匹配节点的节点集合,顺序为节点在文档中出现的顺序。这是最常用到的结果类型)


下一节是 XMLHttpRequest

XML、XMLDOM、XMLHttpRequest 详解(二)

阅读更多
版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/csdn_yudong/article/details/79979339
文章标签: xml xhr xmldom dom w3c
个人分类: 前端 综合
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭