【JavaScript】JavaScriptまとめ⑦（DOM）

# 概要
JavaScriptの理解を深めるため、
[<img width="200" alt="51vlB3JskRL.jpg" src="https://m.media-amazon.com/images/I/41vZUGXaYML._SX350_BO1,204,203,200_.jpg">](https://www.amazon.co.jp/%E3%82%B9%E3%83%A9%E3%82%B9%E3%83%A9%E3%82%8F%E3%81%8B%E3%82%8BJavaScript-%E6%96%B0%E7%89%88-%E6%A1%9C%E5%BA%AD-%E6%B4%8B%E4%B9%8B/dp/4798173266/ref=sr_1_15?crid=341O9294X80Z7&keywords=javascript&qid=1679238835&sprefix=javas%2Caps%2C185&sr=8-15)
で学習した内容を記載していく。

本記事では、DOMについて記載する。


### DOM
#### DOMの役割
<INS>**・ボタンを押したら、ボタンのデザインが変わる
・ページをスクロールすると、途中で広告バナーが表示される
・入力フォームで、内容を間違えると「正しく入力してください」とエラーが表示される**

`DOM`を使うことで、このように動的な表現力を手に入れることができる。

以下の内容でHTMLファイルを保存する。保存後、HTMLファイルをブラウザで開く。
```sample.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="utf-8">
    </head>
    <body>
        <div id="sample">オリジナルのテキストです</div>
    </body>
</html>
```
　次にブラウザのデベロッパーツールのコンソールを開き、以下のコードを実行する。
```sample.js
const tag = window.document.querySelector('#sample');
tag.textContent = ''テキストを書き換えます;
```
このように、DOMを一言で表すと「JavaScriptからHTMLを操作するための仕組み」ということになる。
DOMはWindowオブジェクト配下のDocumentオブジェクトなので、DOMに関するすべての機能は、`window.document`オブジェクトから呼び出して使う。
#### DOMはツリー構造
HTMLは要素の中に、別の要素を入れ子のように組み立てることができる。DOMはHTMLの文書構造をそのまま表現しているので、同様に要素同士が親子関係のようなツリー構造になっている。
```dom_tree.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="utf-8">
        <title>ページのタイトル</title>
    </head>
    <body>
        <div>
            <p>テキストです。<strong>強調します。</strong></p>
            <p>他のテキストです。</p>
        </div>
    </body>
</html>
```
#### ノードの種類
ツリーを構成している１つひとつのHTML要素を`ノード`と呼ぶ。
先ほどの例だと`<html>`はルートノードである。ルートノードはHTMLの中にただ１つだけ存在する。つまり`<html>`がルートノードになる。
そして`<div>`は`<body>`の子ノード。逆に`<body>`は`<div>`タグから見ると親ノードである。`<div>`の下にある`<p>`は兄弟ノードになる。

###　要素を探す
#### 特定要素を探す
HTMLの要素を操作するには、まず操作対象となる要素を探す必要がある。
```
document.querySelector(CSSセレクタ);
```
`querySelector()`は引数にCSSセレクタを渡すと、それに一致する最初の要素を返す。
```queryselector.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="utf-8">
    </head>
    <body>
        <div id="sample">class 属性が sample 1個目</div>
        <div id="sample">class 属性が sample ２個目</div>
        <div id="sample">class 属性が sample ３個目</div>
    </body>
</html>
```
```queryselector.js
const element = document.querySelector('.sample');
console.log(element);
```
#### すべての特定要素を探す
CSSセレクタにマッチするすべての要素を取得したい場合は`querySelectorAll()`を使う。
```
document.querySelectorAll(CSSセレクタ);
```
querySelectorAll()は複数のHTML要素を取得するため、戻り値は`NodeList`と呼ばれる特殊な配列形式のデータになる。
NodeListは通常の配列と同じように`length`プロパティで配列内の数を調べることができる。
```queryselectorall.js
const elements = document.querySelectorAll('.sample');
console.log(elements.length);
```
配列のそれぞれの要素にアクセスするためには`for...of`を使ってループ処理をする。
```queryselectorall_for.js
const elements = document.querySelectorAll('.sample');
for (const element of elements) {
    console.log(element); // <div>タグが順次出力される
}
```
あるいは、配列のインデックスで直接アクセスすることもできる。
```queryselectorall_index.js
const elements = document.querySelectorAll('.sample');
console.log(elements[0]);
```

### 要素を変更する
#### 要素のテキストを変更する
テキストを変更するには`textContent`プロパティを使う。
```
要素.textContent = '変更したいテキスト';
```
textContentを使ってテキストを変更してみる。
```text_content.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
    </head>
    <body>
        <p>テキストです</p>
    </body>
</html>
```
作成したHTMLファイルをブラウザで開いてデベロッパーツールのコンソールから以下のコードを実行する。
```text_content.js
const p = document.querySelector('p');
p.textContent = '変更します';
```
#### 要素の属性を変更する
```
// 指定属性の値を参照する
要素.属性名

// 指定属性の値を変更する
要素.属性名 = '変更したい値';
```
```attr.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
    </head>
    <body>
        <img src="sample.jpg" width="200" height="200">
    </body>
</html>
```
画像の高さと幅が200pxの正方形のものを使って表示している。それを以下のコードを実行し、横長にしてみる。
```attr.js
const img = document.querySelector('img');
img.width = 300;
```
#### カスタムデータ属性について
あらかじめ定義されている属性以外に、任意の属性を指定することができる`カスタムデータ属性`というものがある。`data-`から始まる属性名で表現され、どのようなタグにも使うことができる。
```
data-任意の名前="値"
```
例えば以下のようにユーザ名を表示する<div>要素に、ユーザIDやユーザ名をカスタムデータ属性で定義する。これはブラウザ上には表示されないので、ユーザが目にすることはない。
```attr.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
    </head>
    <body>
        <div class="profile" data-id="100" data-user-name="zaru">zaru</div>
    </body>
</html>
```
次にデベロッパーツールで以下のコードを実行する。ユーザには見えていなかった属性を、`dataset`というプロパティを使って参照することができる。
```data-attr.js
const profile = document.querySelector('.profile');
console.log(profile.dataset.id);
console.log(profile.dataset.userName);
```
また、通常の属性と同じように値の書き換えもできる。
```data_attr_write.js
const profile = document.querySelector('.profile');
profile.dataset.id = 999;
profile.dataset.userName = 'new zaru';
console.log(profile.dataset.id);
console.log(profile);
```

### CSSを変更する
`Styleオブジェクト`を使うことでCSSを直接変更することができる。`style`の後にドット（`.`)でCSSのプロパティ名をつなげて操作する。
```
要素.style.CSSプロパティ名 = '値';
```
```style.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
    </head>
    <body>
        <p>テキストです</p>
    </body>
</html>
```
p要素のstyleオブジェクトのfontSizeプロパティに36pxを指定する。
```style1.js
const p = document.querySelector('p');
p.style.fontSize = '36px';
```
#### class属性を変更する
フォントのサイズや色、背景色など複数のCSSを変更しようとすると、複数のコードを書くようになりコードが煩雑になる。
```style2.js
const p = document.querySelector('p');
// 装飾の変更するコードが増えて読みにくい状態
p.style.fontSize = '36px';
p.style.color = 'red';
p.style.backgroundColor = 'yellow';
```
そこで、あらかじめCSSでスタイルをclass名で定義しておき、HTML要素のclass属性を変更することで見た目を切り替える方法がよく使われる。
例として.normalクラスと.warningクラスという２つのクラスを作る。通常は.normalクラスにし、警告を表現したいときには.warningクラスに切り替えるようにしてみる。
```class_attr.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
        <style>
        .normal {
            font-size: 16px;
            color: black;
            background-color: white;
        }
        .warning {
            font-size: 36px;
            color: red;
            background-color: yellow;
        }
    </head>
    <body>
        <p class="normal">テキストです</p>
    </body>
</html>
```
要素のクラスを変更するには`ClassListオブジェクト`を使う。.normalクラスから.warningクラスに変えたいので、まずは.normalクラスを削除する。削除するには`remove()`メソッドを使う。
```
要素.classList.remove('削除するクラス名');
```
```class_attr1.js
const element = document.querySelector('p');
element.classList.remove('normal');
```
続いて.warningクラスを追加する。追加するには`add()`メソッドを使う。
```
要素.classList.add('追加するクラス名');
```
```class_attr2.js
const element = document.querySelector('p');
element.classList.add('warning');
```
add()とremove()を使ったコードは`replace()`で書き換えることもできる。
```class_attr3.js
const element = document.querySelector('p');
element.classList.replace('normal', 'warning');
```

### 要素を作成する
DOM要素はあらかじめHTMLファイルに書かれた要素だけではなく、`createElement()`メソッドを使って自由に作ることもできる。引数にタグの名前を渡すと要素オブジェクトが作成される。
```
document.createElement('タグの名前');
```
```create_element.js
const element = document.createElement('p');
console.log(element);
```
しかし要素オブジェクトを作成しただけでは、まだブラウザに表示される要素にはなっていない。実際に表示させるためにはいくつかのメソッドを使って、HTMLに要素オブジェクトを反映させる必要がある。
#### 要素を末尾に追加する
createElementで作った要素オブジェクトをブラウザに表示するには`append()`というメソッドを使う。append()は指定要素の子要素として、末尾に要素を追加することができる。
```
親要素.append(追加する要素);
```
```append.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
    </head>
    <body>
        <div class="content">
            <p>最初からある要素です</p>
        </div>
    </body>
</html>
```
デベロッパーツールのコンソールで以下のコードを実行すると<p>要素が追加される。
```append.js
const newElement = document.createElement('p');
newElement.textContent = '新しく追加しました';

const content = document.querySelector('.content');
content.append(newElement);
```
まずcreateElement()メソッドで`<p>`要素オブジェクトを作成し、textContentプロパティでテキストを設定する。
作成した`<p>`要素オブジェクトをappend()を使い.contentクラスの`<div>`要素の子要素として末尾に追加する。
```append.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
    </head>
    <body>
        <div class="content">
            <p>最初からある要素です</p>
            <p>新しく追加しました</p>　←追加された要素
        </div>
    </body>
</html>
```
逆に子要素の先頭に追加するには`prepend()`を使う。
```
親要素.prepend(追加する要素);
```
```prepend.js
const newElement = document.createElement('p');
newElement.textContent = '先頭に追加します';

const content = document.querySelector('.content');
content.prepend(newElement);
```
すると、一番先頭に新しい要素が追加されているのを確認できる。
```append.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
    </head>
    <body>
        <div class="content">
            <p>先頭に追加します</p>　←追加された要素
            <p>最初からある要素です</p>
        </div>
    </body>
</html>
```
#### 指定要素の前や後ろに追加する
指定要素の前後に追加するには`before()`と`after()`を使う。その名前の通り、`before()`は指定要素の前に要素を追加し、`after()`は後ろに追加する。
```
指定要素.before(追加する要素);
指定要素.after(追加する要素);
```
```before_after.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
    </head>
    <body>
        <div class="content">
            <!-- ここに新しい要素を追加したい -->
            <p class="first">最初からある要素です</p>
        </div>
    </body>
</html>
```
今回は親要素は使わない。前後の軸となる.first要素さえ取得できれば追加することができる。
```before.after.js
const newElement = document.createElement('p');
newElement.textContent = '新しく追加しました';

const firstElement = document.querySelector('.first');
firstElement.before(newElement);
```
このコードを実行すると、以下のHTMLと同じ状態になる。
```before_after.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
    </head>
    <body>
        <div class="content">
            <p>新しく追加しました</p>　←追加された要素
            <p class="first">最初からある要素です</p>
        </div>
    </body>
</html>
```
#### 要素を削除する
```
削除したい要素.remove();
```
.firstクラスの`<p>`要素を削除する。
```remove.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
    </head>
    <body>
        <div class="content">
            <!-- ↓この要素を削除したい -->
            <p class="first">最初からある要素です</p>
        </div>
    </body>
</html>
```
ブラウザのデベロッパーツールコンソールで以下のコードを実行する。
```remove.js
const firstElement = document.querySelector('.first');
firstElement.remove();
```
このコードを実行すると、以下のHTMLと同じ状態になる。
```remove.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
    </head>
    <body>
        <div class="content">
        </div>
    </body>
</html>
```
#### 要素を置換する
既存要素を新しい要素で置き換えるには`replaceWith()`を使う。
```
置き換えたい要素.replaceWith(置き換える要素);
```
```replace.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
    </head>
    <body>
        <div class="content">
            <!-- ↓この要素を別の要素に置換したい -->
            <p class="first">最初からある要素です</p>
        </div>
    </body>
</html>
```
デベロッパーツールのコンソールで以下のコードを実行する。
```replace.js
const newElement = document.createElement('p');
newElement.textContent = '置き換える要素です';

const firstElement = document.querySelector('.first');
firstElement.replaceWith(newElement);
```
このコードを実行すると、以下のHTMLと同じ状態になる。
```replace.html
<!DOCTYPE html>
<html>
    <head>
        <meta chaset="UTF-8">
    </head>
    <body>
        <div class="content">
            <p>置き換える要素です</p>
        </div>
    </body>
</html>
```