DOM

# DOMとは
Document Object Model（ドキュメントオブジェクトモデル）の略で、HTMLやXMLの文書を扱うためのAPIです。
DOMではHTMLやXMLのデータ内の要素をオブジェクトとして扱い、オブジェクトが階層的に組み合わされたものとして識別します。

# ノード
DOMでは識別されたオブジェクトのことをノードと呼びます。
ノードにはいくつか種類があり、それぞれが役割を担っています。

|ノードの種類  |概要
|---|--
|ドキュメントノード |HTML文書全体  
|要素ノード |HTMLタグ  
|テキストノード |HTMLタグ以外のテキスト
|属性ノード |HTMLタグの属性（classやidなど）   
|コメントノード |コメントアウトされたテキスト  

これらのノードが階層的に組み合わさり、木構造を形成したものをDOMツリーと呼びます。

# 階層構造
DOMではノードが組み合わさることで階層構造を形成します。
以下のようなコードがあったとします。
```html
<html>
<head>
    <title>sample</title>
    <meta chareset="utf-8">
</head>
<body>
    <p id="test">
    <!--コメント-->
    これは<strong>サンプルテキスト</strong>です。
    </p>
</body>
</html>
```
階層構造で表すと下記のようになります。
```
Document
└ html
   ├ head
   │   ├ 空白
   │   ├ title
   │   │   └ sample
   │   ├ 空白   
   │   ├ 　meta
   │   └ 空白
   └ body
     ├ 空白
     └ p ─ id
       ├ 空白
       ├ コメント
       ├ 空白
       ├ これは
       ├ strong
       │ └ サンプルテキスト
       ├ です。
       └ 空白
```
DOMツリーでは空白はテキストとして扱われ、空白ノードというテキストノードの一つとして分類されます。

# DOM操作
ノードにはHTMLと同じように親子関係があります。
bodyタグを基準として考えると、htmlタグは、bodyの親ノードにあたり、headタグは兄弟ノード、pタグは子孫ノードにあたります。

## プロパティ
ノードの参照方法として以下のプロパティがありますが、こちらのプロパティでは空白ノードを含めて参照してしまうため、効率があまり良くありません。
|プロパティ  |概要
|---|--
|parentNode |親ノードへの参照。（参照先がDocumentオブジェクトの場合はnullを返す）  
|childNodes |子ノードへの参照。
|firstChild |最初の子ノード。（子ノードを持たない場合はnullを返す）
|lastChild |最後の子ノード。（子ノードを持たない場合はnullを返す）
|nextSibling |兄弟ノード内の次のノード。（次の兄弟ノードを持たない場合はnullを返す）  
|previousSibling |兄弟ノード内の一つ前のノード。（前の兄弟ノードを持たない場合はnullを返す）  
|nodeType |ノードのノードタイプを返す。
|nodeValue|テキストノードの値を返す。
|nodeName|要素ノードの要素名を返す。（pタグの場合はp）

そのため、テキストノードを無視して要素ノードを取り出すためのプロパティがあります。
|プロパティ  |概要
|---|--
|parentElement |親ノードへの参照。（参照先がDocumentオブジェクトの場合はnullを返す）  
|children |子要素ノードへの参照を格納するHTMLCollection
|firstElementChild |最初の子要素ノード。（子要素ノードを持たない場合はnullを返す）
|lastElementChild |最後の子要素ノード。（子要素ノードを持たない場合はnullを返す）
|nextElementSibling |兄弟要素ノード内の次の要素ノード。（次の兄弟要素ノードを持たない場合はnullを返す）  
|previousElementSibling |兄弟要素ノード内の一つ前のノード。（前の兄弟要素ノードを持たない場合はnullを返す）  
|childElementCount |子要素ノードの数。



## メソッド
上記のプロパティはdocumentを基準としてノードを取得しているためかなり不便です。
そのため任意の要素ノードを取得するメソッドがあります。
|メソッド  |概要
|---|--
|getElementById('id名') |指定したid名と一致する要素ノードを取得 
|getElementsByClassName('クラス名') |指定したクラス名と一致した要素をHTMLCollectionとして取得
|getElementsTagName('タグ名') |指定したタグ名と一致した要素をHTMLCollectionとして取得
|getElementsByName('属性名')|指定した属性名と一致した要素をNodeListとして取得
|quelySelector('セレクタ') |指定したセレクタと一致した要素の最初のひとつを取得 
|quelySelectorAll('セレクタ')|指定したセレクタと一致した要素を格納したNodeListとして取得 

# HTMLCollectionとNodeListの違い
今まで何回か出てきたHTMLCollectionとNodeListですが、どちらも要素を配列のように格納したものですが、挙動が異なる点がいくつか存在します。

## HTMLCollectionは動的、NodeListは静的
HTMLCollectionは後から要素が増減した場合、動的に反映されるのに比べて、NodeListは要素が後から増減しても反映されることはありません。
``` HTML
<ul id="list">
    <li class="listitem">テキスト</li>
    <li class="listitem">テキスト</li>
    <li class="listitem">テキスト</li>
    <li class="listitem">テキスト</li>
</ul>
```
``` JavaScript
const item = document.getElementsByClassName('listitem');
console.log(item); // => 4

//HTMLCollection
const newitem = document.createElement('li');
newitem.innerText = 'テキスト';
newitem.classList.add('listitem');
const list = document.getElementById('list');
list.appendChild(newitem);
console.log(item.length); // => 5

//NodeList
const newList = document.createElement('li');
newList.innerText = 'テキスト';
newList.classList.add('listitem');
const list = document.getElementById('list');
list.appendChild(newList);
console.log(item.length); // => 4
```

## メソッドの違い
HTMLCollectionとNodeListでは、使用できるメソッドが異なります。
NodeListでは、forEachメソッドが使用できますが、HTMLCollectionに対しては使用できないといった違いがあります。