HTMLDocument と XMLDocument を見分ける方法
HTMLDocument と XMLDocument を見分ける方法を模索していました。
2008-10-12追記: 内容を大幅に更新しました
何がしたいのか
uupaa-selector.js version 1.2では、HTMLDocuemnt と XMLDocument で、CSSセレクタの挙動が変化します。
- HTMLDocuemnt なら 小文字の a タグ と 大文字の A タグ は一緒。 uu.css("a"); で <a>, <A> がヒットする
- XMLDocument なら タグの大小は区別する。 uu.css("a"); なら <a> のみヒットする
これを実装するためには、XMLDocument と HTMLDocuemnt を見分けたうえで動作する必要があります。
どこを調べれば、XMLDocument を区別できるのか調べてみた
text/html, elm = document
Browser | elm instanceof HTMLDocument | elm instanceof XMLDocument | elm.body |
Firefox2 | true | false | HTMLBodyElement |
Firefox3 | true | false | HTMLBodyElement |
IE6 | 定義されていません | 定義されていません | [Object] |
IE8β2 | 定義されていません | 定義されていません | HTMLBodyElement |
Safari3.1 | true | true | HTMLBodyElement |
Chrome | true | true | HTMLBodyElement |
Opera9.2x | true | 定義されていません | HTMLBodyElement |
Opera9.6β | true | false | HTMLBodyElement |
elm instanceof HTMLDocument === true なら、HTMLDocument と判断できそうです。
WebKit(Safari, Chrome)は、HTMLDocument と XMLDocument を多重にimplementしているのでしょうか。なかなか面白いですね。
application/xml, elm = document
Browser | elm instanceof HTMLDocument | elm instanceof XMLDocument | elm.body |
Firefox2 | false | true | undefined |
Firefox3 | false | true | undefined |
IE6 | - | - | - |
IE8β2 | - | - | - |
Safari3.1 | false | true | HTMLBodyElement |
Chrome | false | true | HTMLBodyElement |
Opera9.2x | true | 定義されていません | HTMLBodyElement |
Opera9.6β | false | true | undefined |
elm instanceof HTMLDocument === false なら、XMLDocument と判断できそうです。
さて、IE, Opera をどうするか。
- IEは、application/xml をレンダリングしませんが、サイ本によると Node.selectNodes は XML文書でしか存在しないメソッドらしいのでこれが使えそう。
- Opera9.2x は、HTMLDocumentが常にtrueなので使えず。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml">
-
-
- os0xさんからのコメントで、document.createElement('p').tagName === 'P' が使えるとの情報が。
-
できた
最初の版
function isXMLDocument(elm) { return uu.ua.ie ? !!elm.selectNodes : // IE (uu.ua.opera && !uu.ua.opera95) ? /XHTML/i.test(elm.doctype.publicId) : // Opera9.2x !(elm instanceof HTMLDocument); // Gecko, WebKit, Opera95+ }
os0xさんからの情報を元に改善された版
function isXMLDocument(elm) { return uu.ua.ie ? !!elm.selectNodes : // IE (uu.ua.opera && !uu.ua.opera95) ? elm.createElement("p").tagName !== "P" : // Opera9.2x !(elm instanceof HTMLDocument); // Gecko, WebKit, Opera95+ }
参考までにjQueryの実装
/* jQuery 1.2.6 - New Wave Javascript * Copyright (c) 2008 John Resig (jquery.com) */ // check if an element is in a (or is an) XML document isXMLDoc: function( elem ) { return elem.documentElement && !elem.body || elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; },
反省会
- Gecko系なら、document.contentType === "text/html" という方法もある。 http://developer.mozilla.org/ja/DOM/document
- Operaって…
ここから下は、2008-10-12 追記分
再調査した。
こんな感じのスクリプトを元に、 header('Content-Type: text/xml'); の部分をちょこちょこ替えてテストします。
<?php header('Content-Type: text/xml'); $rv =<<<EOD <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>test</title> </head> <body>text/xml <script> function judge() { var rv = []; try { rv.push(document instanceof HTMLDocument); } catch(e) { rv.push("fail HTMLDocument"); } try { rv.push(document instanceof XMLDocument); } catch(e) { rv.push("fail XMLDocument"); } try { rv.push(document.body); } catch(e) { rv.push("fail document.body"); } try { rv.push(document.createElement("p").tagName !== document.createElement("P").tagName); } catch(e) { rv.push("fail p !== P"); } try { alert(rv.join(",")); } catch(e) { ; } return rv.join(","); } judge(); </script> </body> </html> EOD; print $rv; ?>
Content-Type: text/html, elm = document
Browser | elm instanceof HTMLDocument | elm instanceof XMLDocument | elm.body | judge() |
Firefox2 | true | false | HTMLBodyElement | false |
Firefox3 | true | false | HTMLBodyElement | false |
IE6 | × | × | [object] | false |
IE8β2 | × | × | HTMLBodyElement | false |
Safari3.1 | true | true | HTMLBodyElement | false |
Chrome | true | true | HTMLBodyElement | false |
Opera9.2x | true | × | HTMLBodyElement | false |
Opera9.6β | true | false | HTMLBodyElement | false |
Content-Type: text/xml, elm = document
Browser | elm instanceof HTMLDocument | elm instanceof XMLDocument | elm.body | judge() |
Firefox2 | false | true | undefined | true |
Firefox3 | false | true | undefined | true |
IE6 | × | × | × | × |
IE8β2 | × | × | × | × |
Safari3.1 | false | true | HTMLBodyElement | true |
Chrome | false | true | HTMLBodyElement | true |
Opera9.2x | true | × | HTMLBodyElement | true |
Opera9.6β | false | true | undefined | true |
Content-Type: application/xml, elm = document
Browser | elm instanceof HTMLDocument | elm instanceof XMLDocument | elm.body | judge() |
Firefox2 | false | true | undefined | true |
Firefox3 | false | true | undefined | true |
IE6 | × | × | × | × |
IE8β2 | × | × | × | × |
Safari3.1 | false | true | HTMLBodyElement | true |
Chrome | false | true | HTMLBodyElement | true |
Opera9.2x | true | × | HTMLBodyElement | true |
Opera9.6β | false | true | undefined | true |
Content-Type: application/xhtml+xml, elm = document
Browser | elm instanceof HTMLDocument | elm instanceof XMLDocument | elm.body | judge() |
Firefox2 | true | false | HTMLBodyElement | true |
Firefox3 | true | false | HTMLBodyElement | true |
IE6 | × | × | ダウンロード(不明なMIMEタイプ) | × |
IE8β2 | × | × | ダウンロード(不明なMIMEタイプ) | × |
Safari3.1 | false | true | HTMLBodyElement | true |
Chrome | false | true | HTMLBodyElement | true |
Opera9.2x | true | × | HTMLBodyElement | true |
Opera9.6β | true | false | HTMLBodyElement | true |
結論(take2)
- id:os0xさんをリスペクト。
できた(take2)
os0xさんからの更なる情報を元に改善された版
function isXMLDocument(elm) { return elm.createElement("p").tagName !== elm.createElement("P").tagName; }
反省会(take2)
- 手抜きとかじゃなくて、リアルに勘違いしてました。ごめんなさい。
- 今日中に、p !== P 方式に差し替えた uupaa-selector.js version 1.3 をリリースする予定です。