Ruby's Louvre

每天学习一点点算法

导航

判断是否XML文档

xhtml的昙花一现带来不少问题,它的目的是让html表现得更像xml,所以才叫xhtml。但是出师未捷身先死,我们讨论一下如何判定文档是XML吧。

印象中jQuery对此方法重复实现了许多次,应该比较权威,也说明这判定比较难搞。看jQuery1.42的实现:

     var isXML = function(elem){
            // documentElement is verified for cases where it doesn't yet exist
            // (such as loading iframes in IE - #4833)
            var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
            return documentElement ? documentElement.nodeName !== "HTML" : false;
        };

好,做一个实验:

  window.onload = function(){
        try{
          var doc = document.implementation.createDocument(null, 'HTML', null);//只限标准浏览器,创建一个XML文档
          alert(doc.documentElement)
          alert(isXML(doc))//应该返回true
        }catch(e){
          alert("不支持creatDocument方法")
        }
    }

另一个实验:

评测结果,它能判定XHTML文档,但无法判定第一个元素为HTML的XML文档。

看来我们需要一些更可靠的特征侦探。我们知道IE的HTML不支持xpath,而XML支持,这个可以利用。对于标准浏览器,参见我另一篇博文《javascript 跨文档调用技术》提到的一系列方法。既然有createHTMLDocument,应该拥有HTMLDocument这个对象,标准浏览器向来比较慷慨,暴露了许多比较底层的方法供我们扩展,如__proto__, Node,Window, Element, HTMLElement什么的。测试一下,真的有这东西

在火狐官网还看到这样一个判定,这是基于XUL的,仅对火狐有效。

//https://developer.mozilla.org/En/XML/Identifying_XML_elements_and_documents
function isXMLDoc (doc) { 
    var Ci = Components.interfaces;
    // Remove the second condition if only wish to test for XML, not XUL
    return (doc instanceof Ci.nsIDOMXMLDocument)||
           (doc instanceof Ci.nsIDOMXULDocument);
}

不管怎么样,既然获知HTMLDocument这个类,就简单了。下面是我的实现:

      var isXML = (function(){
        if(-[1,]){
          return function(doc){
            return  !(doc instanceof HTMLDocument)
          }
        }else{
          return function(doc){
            return "selectNodes" in doc
          }
        }
      })();

下面是测试代码:

      var createXML = function (str) {
        if (typeof DOMParser !== "undefined") {
          return (new DOMParser()).parseFromString(str, "application/xml");
        }else if (ActiveXObject) {
          var xml = new ActiveXObject("Microsoft.XMLDOM");
          xml.async="false";
          xml.loadXML(str);
          return xml
        }
      }

      window.onload = function(){
        var xml = createXML('<HTML><body><book><title>司徒正美</title></book></body></HTML>');   
        alert(isXML(xml))
      }

可能有人会问,为什么不用XMLDocumet?好问题,因为XMLDocumet在opera中支持得比较晚。大抵是opera9.6才支持,这是我以前在日本博客看到的数据。不过opera是个小众的浏览器,你大可以不管它比较旧的版本,而直接用XMLDocument。

补充一下,我以前是使用以下判定的,一样可行,但是要创建两个元素对象。在IE7中,没有加入DOM树的元素节点不会被回收,需要特殊处理一下,比较麻烦,遂放弃之。

   var isXML =  function (doc) {
        return doc.createElement("p").nodeName !== doc.createElement("P").nodeName;
      };
       // Safari 2 missing document.compatMode property
  // makes harder to detect Quirks vs. Strict mode
  var isQuirks =
    function(document) {
      return (document.compatMode ?
        (document.compatMode.indexOf('CSS') < 0) :
        (function() {
          var div = document.createElement('div'),
            isStrict = div.style &&
              (div.style.width = 1) &&
              div.style.width != '1px';
          div = null;
          return !isStrict;
        })());
    },

  // XML is functional in W3C browsers
  isXML = 'xmlVersion' in doc ?
    function(document) {
      return !!document.xmlVersion ||
        (/xml$/).test(document.contentType) ||
        !(/html/i).test(document.documentElement.nodeName);
    } :
    function(document) {
      return document.firstChild.nodeType == 7 &&
        (/xml/i).test(document.firstChild.nodeName) ||
        !(/html/i).test(document.documentElement.nodeName);
    };

  // reset and reused dynamically for each selection
  isQuirksMode = isQuirks(doc),

  // reset and reused dynamically for each selection
  isXMLDocument = isXML(doc),
    function isXML(context) {
          context = context.ownerDocument || document;
          return context.createElement("p").nodeName !== context.createElement("P").nodeName
        }

posted on 2010-03-14 05:53  司徒正美  阅读(3248)  评论(1编辑  收藏  举报