#参照
パーフェクトJavaScript
#DOM
-
DOMとは、HTMLドキュメントやXMLドキュメントをプログラムから利用するためのAPI。
-
DOMではHTMLドキュメントやXMLドキュメントを「オブジェクトのツリー状の集合」として取り扱う。このツリーをDOMツリーと呼ぶ。
-
DOMツリーの一つ一つのオブジェクトはノードと呼ばれる。ある1つのノードから他のノードを参照する時は、親ノード、子ノード、兄弟ノード、先祖ノード、子孫ノードなどと呼ぶ。
-
DOMの仕様は、Level1〜3がW3Cにより定義されている。
#タグと要素とノード
- タグ (文書構造を指定するためのマークアップとして記述する文字列)
- 要素とノード (要素とノードは継承関係 ノードがスーパータイプ)
#DOM操作
[なぜやるのか]
-
インタラクティブな機能
-
ある特定のDOM操作を選択しそのDOMの内容や属性を書き換えたり、新しい機能のDOM要素を作成したりして、ユーザーに視覚的なフィードバックを与える。
[手順]
- 1.ノードの選択
- 2.ノードの作成
- 3.ノードの変更
- 4.ノードの削除
#Documentオブジェクト
-
DOMツリー構造のルートノード
-
Documentオブジェクトは、そのHTMLドキュメント全体を表現するオブジェクト
-
DocumentオブジェクトはJavaScriptにおいて、documentというグローバル変数でアクセスできる。
#1.ノードの選択
var element = document.getElementById('foo');
-
fooというIDを持つ要素を取得。
-
IDはDOMツリーの中で一意でなければならない。
###タグ名による検索とワイルドカードによる選択
//ドキュメント全体からspan要素だけを取得する
var spanElements = document.getElementByTagName('span');
//ドキュメント全体から全ての要素を取得する
var allElements = document.getElementByTagName('*');
Document.getElementById()は、オブジェクトだけのメソッドだったが、
Element.getElementByTagName()は、DocumentオブジェクトとElementオブジェクト両方のメソッド。
あるElementオブジェクトのgetElementByTagName()メソッドを実行した場合、そのElementオブジェクトの子孫ノードの中から特定のタグ名を持つ要素が取得される。
<body>
<p id="foo">
<span>a</span>
<span>b</span>
<span>c</span>
</p>
<p id="bar">
<apan>x</span>
</p>
<script>
//fooの子孫ノードの中からspan要素を取得する
var fooSpans = foo.getElementsByTagName('span');
alert(foospans.length); //3
//ドキュメント全体からspan要素を取得する
var allSpans = document.getElementsByTagName('span');
alert(allspans.length); //4
</script>
###ライブオブジェクト
<div id="foo">
<span>first</span>
<span>second</span>
</div>
<script>
var elems = document.getElementByTagName('span');
alert(elems.length); //2
var newspan = document.createElement('span');
newSpan.appendChild(document.createTextNode('third'));
var foo = document.getElementById('foo');
foo.appendChild(newSpan);
alert(elems.length); //3
</script>
なぜ、spanを追加する前に取得していたNodeListオブジェクトがspanを追加した後の状態も知っているのか?
ライブオブジェクトは、常に、DOMツリー実体への参照を持っているから、
DOMツリーへ加えられた変更は、ライブオブジェクトからも参照できる。
###ライブオブジェクトを操作する上での注意点
//ライブオブジェクトの罠
<div>sample text</div>
<script>
var divs = document.getElementByTagName('div');
var newDiv;
for(var i = 0; i < divs.length; i++){
newDiv = document.createElement('div');
newDiv.appendChild(document.createTextNode('new div'));
divs[i].appendChild(newDiv);
}
</script>
- div.lengthの値が1つ大きくなり、ループが抜けられなくなる
<div>sample text</div>
<script>
var divs = document.getElementByTagName('div');
var newDiv;
//divs.lengthを最初に取得しておき、それをループ継続条件に使う
for(var i = 0; len = divs.length; i < len; i++){
newDiv = document.createElement('div');
newDiv.appendChild(document.createTextNode('new div'));
divs[i].appendChild(newDiv);
}
</script>
###ライブオブジェクトのパフォーマンス
- ライブオブジェクトは、パフォーマンスを考えると不利な実装
- 一度Arrayに変換してから使う場合の方がパフォーマンスが優れている
- Arrayに変換するにはArray.prototype.slice()メソッドをNodeListオブジェクトに対して適用する
var nodeList = document.getElementByTagName('span');
//NodeListオブジェクトをArrayオブジェクトに変換する
var array = Array.prototype.slice.call(nodeList);
(※IE8以前ではエラーになる)
NodeListをforループで扱う場合は、Arrayオブジェクトに変換してから利用した方が良い(P255)
###クラス名による検索
HTMLElement.getElementsByClassName()メソッド
###関連するノードを参照するためのプロパティ
(※P258)
###XPath
例) 「mainというidが指定されたdiv要素の中にある、contentというclassが指定された3番目のp要素」
(※P261)
evaluateメソッドの第4引数の値と返値の関係
イテレータとスナップショットの違い
###Selectors API
CSSで要素を指定する時と同様の指定方法で要素が取得できる
querySelectorAll() 該当する全てを取得
querySelector() 複数の要素が該当したとしても最初の一要素だけ返す
var a = document.querySelector('#foo');
var b = document.getElementById('#foo');
alert(a === b); //true
var c = document.querySelectorAll('div');
var d = document.getElementByTagName('div');
alert(c[0] === d[0]); //true
querySelectorAll()で取得されるオブジェクトはStaticNodeListオブジェクト。
「NodeListリスト」変更を加えるとHTMLドキュメントに反映
「StaticNodeListオブジェクト」 変更を加えてもHTMLドキュメントに反映されない
StaticNodeListオブジェクトに対して変更を加えてもHTMLドキュメントには反映されないことに注意。
#2.ノードの作成
Document.createElement()メソッド
Document.createTextNode()メソッド
ノードを作成しただけではHTMLドキュメントにとって何の変化もない。
作成したノードをDOMツリーに追加して初めてWebブラウザに表示される。
var elem = document.createElement('div'); //div要素を作成
var text = document.createTextNode('テキストノードを作成する');
document.body.appendChild(elem); //body直下にdiv要素を追加
elem.appendChild(text); //作成したdiv要素にテキストノードを追加
var comment = document.createComment('コメントノード');
document.body.insertBefore(comment,elem); //elemの前にコメントノードを挿入
#3.ノードの内容変更
var newNode = document.createElement('div');
var oldNode = document.getElementById('foo');
var parentNode = oldNode.parentNode;
parentNode.replaceChild(newNode,oldNode);
#4.ノードの削除
var elem = document.getElementById('foo');
elem.parentNode.removeChild(elem);
##もっと簡単にHTMLを変更する innerHTML
- HTML5の仕様
var elem = document.getElementById('foo');
elem.innerHTML = '<div>This is a new div element.</div>';
###textContent
var elem = document.getElementById('foo');
elem.textContent = '<div>Is this a new div element?</div>';
div要素は作成されない このまま文字列としてブラウザに表示される
#DOM操作のパフォーマンス
var parent = document.getElementById('parent');
for (var i = 0; i < 10; i++){
var child = document.createElement('div');
parent.appendChild(child);
}
##DocumentFragmentを利用して再描画を1回にする
var fragment = document.createDocumentFragment();
for(var i = 0; i < 10; i++){
var child = document.createElement('div');
//DocumentFragmentに対して子要素を追加する
fragment.appendChild(child);
}
//実際に追加されるのはDocumentFragmentの小要素だけ
document.getElementById('parent').appendChild(fragment);