DOMのまとめ

#参照
パーフェクト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.ノードの選択

```js
var element = document.getElementById('foo');
```

- fooというIDを持つ要素を取得。

- IDはDOMツリーの中で一意でなければならない。


###タグ名による検索とワイルドカードによる選択

```js
//ドキュメント全体からspan要素だけを取得する
var spanElements = document.getElementByTagName('span');

//ドキュメント全体から全ての要素を取得する
var allElements = document.getElementByTagName('*');
```

Document.getElementById()は、オブジェクトだけのメソッドだったが、

Element.getElementByTagName()は、DocumentオブジェクトとElementオブジェクト両方のメソッド。

あるElementオブジェクトのgetElementByTagName()メソッドを実行した場合、そのElementオブジェクトの子孫ノードの中から特定のタグ名を持つ要素が取得される。

```html
<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>
```

###ライブオブジェクト

```html

<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ツリーへ加えられた変更は、ライブオブジェクトからも参照できる。
```

###ライブオブジェクトを操作する上での注意点

```罠.html

//ライブオブジェクトの罠

<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つ大きくなり、ループが抜けられなくなる


```正しい.html

<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オブジェクトに対して適用する

```js
var nodeList = document.getElementByTagName('span');

//NodeListオブジェクトをArrayオブジェクトに変換する
var array = Array.prototype.slice.call(nodeList);
```

(※IE8以前ではエラーになる)


NodeListをforループで扱う場合は、Arrayオブジェクトに変換してから利用した方が良い(P255)


###クラス名による検索

```js
HTMLElement.getElementsByClassName()メソッド
```

###関連するノードを参照するためのプロパティ

![スクリーンショット 2016-03-05 23.47.52.png](https://qiita-image-store.s3.amazonaws.com/0/62264/6aaf40f9-f913-73e8-a7c8-4615eb435f56.png)

(※P258)


###XPath

例) 「mainというidが指定されたdiv要素の中にある、contentというclassが指定された3番目のp要素」

(※P261) 
evaluateメソッドの第４引数の値と返値の関係
イテレータとスナップショットの違い


###Selectors API

CSSで要素を指定する時と同様の指定方法で要素が取得できる

querySelectorAll()　該当する全てを取得
querySelector()　複数の要素が該当したとしても最初の一要素だけ返す

```js

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.ノードの作成

```js

Document.createElement()メソッド
Document.createTextNode()メソッド
```

ノードを作成しただけではHTMLドキュメントにとって何の変化もない。
作成したノードをDOMツリーに追加して初めてWebブラウザに表示される。


```js

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.ノードの内容変更

```js

var newNode = document.createElement('div');
var oldNode = document.getElementById('foo');
var parentNode = oldNode.parentNode;
parentNode.replaceChild(newNode,oldNode);
```

#4.ノードの削除

```js
var elem = document.getElementById('foo');
elem.parentNode.removeChild(elem);
```

---
---


##もっと簡単にHTMLを変更する　innerHTML

- HTML5の仕様

```js
var elem = document.getElementById('foo');
elem.innerHTML = '<div>This is a new div element.</div>';
```

###textContent

```js
var elem = document.getElementById('foo');
elem.textContent = '<div>Is this a new div element?</div>';
```
div要素は作成されない　このまま文字列としてブラウザに表示される


#DOM操作のパフォーマンス



```パフォーマンスが悪い.js
var parent = document.getElementById('parent');
for (var i = 0; i < 10; i++){
 var child = document.createElement('div');
 parent.appendChild(child);
}
```

##DocumentFragmentを利用して再描画を1回にする

```パフォーマンスが良い.js
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);
```