DOMについて学んでみた。
JavaScriptのクロスブラウザ対応は、ほとんどDOMに起因するといっても過言ではないぐらい、「IE」と「他のモダンブラウザ」の仕様が違う。jQueryが出る前はみんなこういう戦いに疲弊したんだとしみじみ思った。jQueryをMITライセンスで公開してくれたJohn Resigはネ申。そしてIE爆発しろ。
漢は黙ってブラウザ判定
DOMから始めるJavaScriptモダン・スクリプティング - 第1回 DOMから始めるモダン・スクリプティングの世界...:ITproより。
if(navigator.appName == "Netscape") { /* Netscape Navigatorの場合 */ var version = parseFloat(navigator.appVersion); if(version >= 4) { /* Ver4の場合 */ } else { /* Ver4未満の場合 */ } } else if(navigator.appName == "Microsoft Internet Explorer") { /* Internet Explorerの場合 */ var ua = navigator.userAgent; var offset = ua.indexOf("MSIE"); var len = ua.indexOf(";", offset); var version = parseFloat(ua.substring(offset+5, len)); if(version >= 5) { /* Ver5以上の場合 */ } else if(version >= 4) { /* Ver4の場合 */ } else { /* Ver4未満の場合 */ } } else { /* それ以外のブラウザの処理 */ }
なんぞこれ・・・。バッドノウハウがすごいな・・・。Web1.0的な時代の頃はこうだったのか・・・?
ドキュメントツリーモデル
何でこんなに違うんだろう・・・。今はもう少しマシになったのかもしれないが。
正しいHTMLとドキュメントツリーを理解しよう - @IT自分戦略研究所
結局元が違うから同じAPIでアクセスしても取れる値が違ってくるという話のようだ。
ノードの種類
- 要素ノード
- HTMLのタグ。簡単に言ってしまえば。
- テキストノード
- タグではない文字の部分。
- ホワイトスペースノード(IE以外)
- 改行やタブやスペースもノードとして解釈してしまうが、ブラウザによって出現位置が異なるらしい・・・。
DOMスクリプティングでは、ホワイトスペースノードの存在の有無に影響しない手法で要素を特定する必要がある。
・・・つーことはループ回して再帰的に自分を探して目的のノードを探すプログラミングをしなくてはならないのか。
DOMを使って特定要素にアクセス
この3種類しかAPIがない。
- document.getElementById('id属性値');
- 引数に与えるid属性値の大文字・小文字を区別するが、IE6と7が区別しないので結局全部小文字で。
- document.getElementsByTagName('要素名');
- document.getElementsByName('name属性');
DOMを使って相対的にアクセス
特定した要素ノードを元にあっちいったりこっちいったりする。
- 子要素(childNodes/firstChild/lastChild)
- 親要素(parentNode)
- 兄弟要素(previousSibling/nextSibling)
- hasChildNodes()
- ノードの存在有無を返す
- nodeType
- ノードの種類を数字で表す
▼ノード参照時におけるエラーハンドリング手法を学ぼう - @IT自分戦略研究所より。
表 ノードの種類とnodeType、nodeName、nodeValueの対応 | |||
ノードの種類 |
nodeType |
nodeName |
nodeValue |
要素ノード |
1 |
タグが大文字でセットされる |
null |
属性ノード |
2 |
属性名 |
属性地 |
テキストノード |
3 |
#text |
テキストの内容がセットされる |
コメントノード |
8 |
#comment |
コメントの内容がセットされる |
ドキュメントノード |
9 |
#document |
null |
クロスブラウザ対応をする為、色んなことをチェックしなくてはならない。テキストを取り出すだけでこんなコードを書く必要があります。こういう非本質的なコードがホント多くなる。
/*authorsboxの要素ノードオブジェクト*/ var authorsbox = document.getElementById('authorsbox'); /*作家名を格納する配列を初期化*/ var authors = new Array(); /*authorsboxの子要素を1つずつ繰り返し処理*/ for( var i=0; i<authorsbox.childNodes.length; i++ ) { /*子要素のノードオブジェクト*/ var node = authorsbox.childNodes.item(i); /*ノードタイプが"要素"でなければ無視*/ if( node.nodeType != 1 ) { continue; } /*テキストノードを子要素に持っていなければ無視*/ if( ! node.hasChildNodes() ) { continue; } /*本当にテキストノードかどうかをチェック*/ if( node.firstChild.nodeType != 3 ) { continue; } /*テキストを抜き出して配列に格納*/ authors.push(node.firstChild.nodeValue); }
要素の属性値
プロパティでアクセスして読み書き自由。よしなにやる。
for(var i in obj) {console.log(i,+":"+ obj[i]}とかでkeyとvalueを見て、適当に変えるコード書いて遊んだほうがよさげ。
要素追加と削除
ここが詳しい。
イベント
同じ要素に同じイベントハンドラを複数定義できずに上書きされる。人数が増えてくるとバグの元。そこでW3C DOMイベントモデル。が、IEが独自のイベントモデルを採用している為W3C DOMに準拠していないのでそこをラップするメソッドが必要。
function addEventListener(element,tyupe,listener) { //W3C DOM if(element.addEventListener) { element.addEventListener(type,listener,false); //IE } else if(element.attachEvent){ element.attachEvent('on' + type,listener); } } function removeEventListener(element,type,listener) { if(element.removeEventListener) { element.removeEventListener(type,listener,false); } else if(element.detachEvent){ element.detachEvent('on' + type,listener); } } function getKeyCode(event) { //IEはkeyCode、firefoxはcharCode return event.keyCode || event.charCode; }
まとめ
原始的なDOMへのアクセスを勉強すればするほど、jQueryのすごさがわかった。JSが楽しくなってきたよー。