判断是否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 }