14 DOM - iruma-tea/dokushujs GitHub Wiki

14. DOM

14.1 DOMインタフェース

JavaScriptからHTMLへの入出力はDOM(Document Object Model)というAPIを通して行う。
DOMは、DOMインタフェースやDOM API
とも呼ばれる。
JavaScriptのソースコード中では直接HTMLを扱うことができないため、DOMインタフェースを持つDOMオブジェクトを通してHTMLの情報を扱う。

14.1.1 DOMツリー

Documentオブジェクトには、HTMLの構造がDOMオブジェクトに変換された状態で、ツリー構造で格納される。
DOMツリーを構成する個々のオブジェクトは**Node(ノード)と呼ばれる。
Nodeには、テキストやHTMLコメント、HTMLタグなどの種別がある。
Nodeの中でもHTMLタグのみを表す場合に
Element(エレメント)**と呼ぶ。Elementとは、Nodeの種別がElementタイプ(HTML要素)のものを指す。

14.1.2 親子関係を表すDOMインタフェース

NodeやElementなどのDOMオブジェクトには、親子関係を持つプロパティがある。
Nodeを取得するためのプロパティ(childNodes)とElementを取得するプロパティ(children)などもその一つ。
NodeかElementのどちらを取得するかによって使い分けるようにする。

プロパティ 戻り値のタイプ 説明
parentElement Element 親のElementを返す
parentNode Node 親のNodeを返す
children HTMLCollection 子Elementを含む配列風オブジェクトを返す
childNodes NodeList 子nodeを含む配列風オブジェクトを返す
firstElementChild Element childrenで取得される配列風オブジェクトの最初の要素を返す
firstChild Node childNodesで取得される配列風オブジェクトの最初の要素を返す
lastElementChild Element childrenで取得される配列風オブジェクトの最後の要素を返す
lastChild Node childNodesで取得される配列風オブジェクトの最後の要素を返す
previousElementSibling Element 自要素と兄弟関係にある1つ前のElementを返す
previousSibling Node 自要素と兄弟関係にある1つ前のNodeを返す
nextElementSibliing Element 自要素と兄弟関係にある1つ後のElementを返す
nextSibling Node 自要素と兄弟関係にある1つ後のNodeを返す

DOMツリー外から取得可能なDOMオブジェクト

プロパティ 戻り値のタイプ 説明
document.body Element HTML内の<body>のElementを返す
document.head Element HTML内の<head>のElementを返す
document.images HTMLCollection HTML内の<img>のElementを含む配列風オブジェクトを返す
document.forms HTMLCollection HTML内のすべての<form>のElementを含む配列風オブジェクトを返す
document.embeds HTMLCollection HTML内のすべての<embed>のElementを含む配列風オブジェクトを返す

14.1.3 特定のElementを取得するDOMインタフェース

DOMインタフェースには、特定のキー情報をもとにしてHTML要素をDOMツリーから取得する方法が用意されている。
それらは、Element(オブジェクト)または、Documentオブジェクト(document)のメソッドとして実装されている。

特定のElementを取得するメソッド

メソッド 戻り値 説明
getElementById("idAttr") Elment | null idの属性値(idAttr)と一致した最初のElementを取得する。
一致する要素がない場合は、nullが返る。
getElementsByClassName("clsAttr") HTMLCollection class属性(clasAttr)を使ってElementが格納された配列風オブジェクトを取得する。
一致する要素がない場合は空のHTMLCollectionが返る。
getElementsByName("nameAttr") NodeList name属性(nameAttr)を使ってElementが格納された配列風オブジェクトを取得する。
一致する要素がない場合は空のHTMLCollectionが返る。
getElementsByTagName("tagName") HTMLCollection タグ(tagName)を使ってElementが格納された配列風オブジェクトを取得する。
一致する要素がない場合は空のHTMLCollectionが返る。
・特定のElementを取得するメソッド

<section id="container">
    <span class="target-cls"></span>
    <div class="list">
        <input name="child1" />
        <input name="child2" />
    </div>
    <p class="target-cls"></p>
</section>

<script>
    const elById = document.getElementById("container");

    const elsByCls = document.getElementsByClassName("target-cls");

    const elsByName = document.getElementsByName("child1");

    const elsByTag = document.getElementsByTagName("p");
</script>

14.1.4 セレクタAPIによるElementの取得

現代のブラウザでは、**セレクタAPI(Selectors API)を使って、柔軟にElementオブジェクトを取得できる。
getElementById,getElementsByClassName,getElementsByName,getElementsByTagNameは取得する条件によってメソッドを使い分ける
必要があるため利便性に欠ける。それに対してセレクタAPIは引数の
セレクタ文字列(selector)**によって取得条件を指定できるため、
現在のJavaScriptでは基本的にこの方法で要素を取得する。

メソッド 戻り値 説明
querySelector(selector) Element セレクタ文字列(selector)に一致した最初のElementを取得する
querySelectorAll(selector) NodeList セレクタ文字列(selector)に一致したすべてのElementを格納した配列風オブジェクトを取得する

セレクタAPIで使用可能なセレクタ文字列

セレクタ文字列 記述例 説明
* * すべてのタグに一致する
E div
<div>タグ</div>
タグ名(E)に一致する
#idAttr #target
<div id="target">タグ</div>
idの属性値(idAttr)に一致する
.classAttr .target
<div class="target">タグ</div>
classの属性値(clsAttr)に一致する
[attr] [disabled]
<input disalbed>
属性値(attr)に一致する
[attr="value"] [type="password"]
<input type="password">
属性名(attr)の属性値(value)に一致する
[attr^="value"] [href^="http"]
href属性の値がhttpから始まる要素に一致する
<a href="http://example.com"\>リンク\</a>
属性名(attr)の属性値(value)に先頭一致する
[attr$="value"] [href$="pdf"]
href属性の値がpdfで終わる要素一致する
<a href="/sample.pdf">PDFリンク</a>
属性名(attr)の属性値(value)に後方一致する
[attr*="value"] [name*=text]
name属性にtextを含む要素が一致する
<input name="text-1">
<input name="text-2">
属性名(attr)の属性値(value)に部分一致する
S1,S2 div, .cls
以下のいれずれのHTMLに一致する
<div></div>
<p class="cls"></p>
セレクタ(S1)またはセレクタ(S2)に一致する。セレクタにおけるOR条件
S1S2 .cls1.cls2
クラス属性にcls1,cls2がついている要素(<h1>)に一致する
<div class="cls1">
<h1 class="cls1 cls2">
<span></span>
</h1>
</div>
セレクタ(S1)かつセレクタ(S2)に一致する。セレクタにおけるAND条件
S1 S2 div span
<div>に含まれる<span>が一致する。<div>
<h1>
<span></span>
</h1>
</div>
セレクタ(S1)内のセレクタ(S2)に一致する。
S1 > S2 div > h1
<div>の子要素<h1>に一致する。
<div>
<h1></h1>
</div>
セレクタ(S1)の子要素にあたるセレクタ(S2)に一致する。
S1 + S2 div + h1
<div>の直後の<h1>に一致する。
<div></div>
<h1></h1>
セレクタ(S1)の直後にあるセレクタ(S2)に一致する。
S1 ~ S2 div ~ h1
<div>の兄弟、かつ後に一致する<h1>に一致する。
<div></div>
<span></span>
<h1></h1>
セレクタ(S1)の兄弟関係でS1よりも後にあるセレクタ(S2)に一致する。
 セレクタAPIを使ったElementの取得

<section id="container">
    <span class="target-cls"></span>
    <div class="list">
        <input name="child1" />
        <input name="child2" />
    </div>
    <p class="target-cls"></p>
</section>

<script>
    const elById = document.querySelector("#container");

    const elsByCls = document.querySelectorAll(".target-cls");
    const elByCls = doucment.querySelector(".target-cls");

    const elsByName = document.querySelectorAll('[name="child1"]')

    const elsByTag = document.querySelectorAll("p");
</script>

14.1.5 祖先要素にさかのぼって検索する

closest メソッドを使うと、親とその親(祖先)と順々にさかのぼって最初に一致する要素(祖先要素と呼びます)を取得できる。

[構文]closest メソッド

let closestElement = element.closest(selector);

closestElement: 自要素(element)の祖先要素をさかのぼって検索したときに最初にセレクタ文字列(selector)に一致するElementを返す。
element: Elementオブジェクトを設定する。
selector: セレクタ文字列を設定する。
 closestメソッドは祖先要素をさかのぼって検索する

<section style="background-color: yellow;">
    これは祖先要素です。
    <div>
        <sapn id="target"></span>
    </div>
</section>
<section style="background-color: orange;">
    これは祖先要素ではありません。
</section>

<script>
    setTimeout(() => {
        const target = document.querySelector("#target");
        const section = target.closest("section");
        section.prepend("発見 -> ");
    }, 2000);
</script>

14.1.6 DOMツリーの構築後にコードを実行

JavaScriptコードを実行時に、まだDOMツリー上に読み込まれていないHTMLタグにはアクセスできない為、注意。

 DOMツリー上に要素がない場合はエラーとなる

<div id="before">この要素はscriptタグより前にあるため取得可能です。</div>

<script>
    const beforeEl = document.querySelector("#before");
    console.log(beforeEl.textContent);
    > この要素はscriptタグより前にあるため取得可能です。

    const afterEl = doucment.querySelector("#after");
    console.log(afterEl.textContent);
    > Uncaugt TypeError: Cannnot read property "textContent" of null;
</script>

<div id="after">この要素はscritタグより後にあるため取得できません。</div>

◇ HTML要素が取得できない場合の対処方法

  • scriptタグをの閉じるタグの直前に記述する。
    <html>
      <body>
          <!--
              HTMLタグの記述
          -->
          <script>/*JavaScriptの実行*/</script>
      </body>
    </html> 
  • DOMContentLoadedイベントまたは、loadイベント内でコードを実行する。
    <html>
        <body>
            <script>
                doucment.addEventListener("DOMContentLoaded", () => {
                    /* この関数内のコードはDOMツリー全体の構築が完了してから実行される */
                    const afterEl = document.querySelector("#after");
                    console.log(afterEl.textContent);
                    > scriptタグの後に記述したHTMLタグも取得可能です。
                });
            </script>
            <div id="after">scriptタグの後に記述したHTMLタグも取得可能です。</div>
        </body>
    </html>
  • defer属性をscriptタグに付与する。
    scriptタグにdefer属性をつけるとことで、DOMツリーの構築後にコードを実行できる。
    また、deferが付いたscriptタグが複数ある場合には、scriptタグの記述順でコードが実行される。
  • scriptタグにtype="module"を追加する。
    <script type="module">とした場合には、defer属性を付与したときと同じ挙動になる。そのため、この場合も
    DOMツリーの構築は全て完了した状態でJavaScriptコードが実行される。

14.2 画面の取得・更新

DOMインタフェースを通して、HTML要素を取得・変更する

14.2.1 要素内のコンテンツの取得・変更

HTMLタグで囲まれた文字列や子要素を取得・変更するときには、ElementオブジェクトのinnerHTMLtextContentにアクセスする。

プロパティ 説明
innerHTML 要素内のHTMLを文字列として取得・変更する。HTMLの取得の場合には、HTMLタグを含む文字列が取得される。また、HTMLの変更の場合は、HTMLタグがきちんと解釈されて画面上に表示される。
textContent 要素内のテキストを取得・変更する。テキスト取得の場合は、HTMLタグが無視される。また、テキスト変更の場合は、HTMLタグはただの文字列として扱われる。
innerText
※非推奨
要素内のテキストがレンダリングされた状態で取得・変更される。変わりにtextContentを使用する
 innerHTMLの使用例
<div id="test">
    <span>Hello World</span>
</div>

<script>
    const testEl = document.querySelector("#test");

    console.log(`innerHTML:${testEl.innerHTML}`);
    > innerHTML:<span>Hello World</span>

    setTimeout(() => {
        testEl.innerHTML = "<h1>Good World</h1>";
    },2000);
</script>
 textContentの使用例
<div id="test">
    <span>Hello World</span>
</div>

<script>
    const testEl = document.querySelector("#test");

    console.log(`textContent:${testEl.textContent}`);
    > textContent: Hello World

    setTimeout(() => {
        testEl.textContent = "<h1>Good World</h1>"
    }, 2000);
</script>

14.2.2 要素の作成

要素の作成方法

  • document.createElementメソッドを使う
  • ダミーの要素のinnerHTMLに作成したいHTMLを挿入する
  • templateタグを使う

◇ document.createElementメソッドを使う

document.createElementメソッドを使うと、DOMツリー上にないElementオブジェクトを新しく作成できる。

 createElementを使った要素の作成

<body>
    <!--新しい要素をここに追加したい-->
    <script>
        const newDiv = document.createElement("div");
        newDiv.textContent = "Hello World";
        document.body.prepend(newDiv);
    </script>
</body>

◇ ダミー要素のinnerHTMLに作成したいHTMLを挿入する

<body>
    <!--新しい要素をここに追加したい-->
    <script>
        // <body>に挿入したいHTMLの構造を文字列で定義
        const htmlStr = `
            <article id="article">
                <h1 id="article-title">記事のタイトル</h1>
                <div class="article-area">
                    <span>タグ:</span><span>スポーツ</span><span>バスケ</span>
                </div>
                <div class="article-body">記事の本文<div>
                <div id="recommend">
                    <h2>おすすめの記事</h2>
                    <a href="#">他の記事</a>
                </div>
            </srticle>
        `;

        function htmlStrToElement(htmlStr) {
            const dummyDiv = document.createElement("div");
            dummyDiv.innerHTML = htmlStr;

            return dummyDiv.firstElementChild; // ダミーの<div>要素の子要素を返す。
        }

        const targetNewElement = htmlStrToElement(htmlStr);
        document.body.prepend(targetNewElement);
    </script>
</body>

◇ templateタグを使う

<body>
    <!--新しい要素をここに追加したい-->
    <template id="tmp1">
        <span>テンプレートHTMLを定義</span>
    </tempalte>
    <script>
        const tmp1 = document.querySelector("#tmp1");
        const targetNewElement = tmp1.content;
        document.body.prepend(targetNewElement);
    </script>
</body>

14.2.3 要素の追加・削除

querySelectorで取得した要素やcreateElementで作成した要素を他の要素に追加するには、
以下のメソッドを使用する。追加する位置などによってメソッドを使い分けてください。

メソッド 説明 導入場所
element.append
(node1[,node2, ...])
要素(element)内の最後の子要素としてNode(Elementを含むNode)を挿入する
Node以外に文字列の挿入も可能
<div>
<span></span>
<!--ここに挿入 -->
</div>
element.prepend
(node1[,node2, ...])
要素(element)内の最初の子要素としてNode(Elementを含むNode)を挿入する
Node以外に文字列の挿入も可能
<div>
<!--ここに挿入 -->
<span></span>
</div>
element.before
(node1[,node2, ...])
要素(element)内の直前の子要素としてNode(Elementを含むNode)を挿入する
Node以外に文字列の挿入も可能
<!--ここに挿入 -->
<div>
<span></span>
</div>
element.after
(node1[,node2, ...])
要素(element)内の直後の子要素としてNode(Elementを含むNode)を挿入する
Node以外に文字列の挿入も可能
<div>
<span></span>
</div>
<!--ここに挿入 -->
targetElement.insertAdjacentElement(position, htmlStr) 要素(targetElement)からの相対的な位置(position)(※1)を指定して
Elementオブジェクト(element)を挿入する。
<!-- beforebegin -->
<div>
<!-- afterbegin -->
<span></span>
<!-- beforeend -->
</div>
<!-- afterend -->
targetElement.insertAdjacentHTML(position, htmlStr) 要素(targetElement)からの相対的な位置(position)(※1)を指定して
HTML文字列(htmlStr)を挿入する。HTML文字列はHTMLに解釈されて画面に表示される。
targetElement.insertAdjacentText(positon, str) 要素(targetElement)からの相対的な位置(position)(※1)を指定して
文字列(Str)を挿入する。HTMLタグはただのテキストとして解釈される。

(※1) positoin

指定値 説明
beforebegin targetElementの直前
afterbegin targetElementの開始タグ直後
beforeend targetElementの終了タグ直前
afterend targetElementの直後
・divタグに対応するElementのメソッド

const div = document.querySelector("div")
div.append("");
メソッド 説明 削除対象
element.remove() 自要素(element)を削除する <div><!-- 削除対象 -->
<span></span>
</div>

14.2.4 要素の属性の取得・変更

要素の属性の取得・変更(Elementのメソッド)

メソッド 説明
getAttribute(name) 属性(name)の値を取得する
getAttributeNames() 属性名の一覧を取得する
setAttribute(name, value) 属性(name)に値(value)を設定する
removeAttribute(name) 属性(name)を削除する
toggleAttribute(name) 属性(name)の取り外しを行う
hasAttributes() 属性を持っているか確認する。何かしらの属性を保持している場合にはtrue、保持していない場合はfalseを返す
hasAttribute(name) 特定の属性(name)を持っているか確認する。保持している場合にはtrue、保持していない場合はfalseを返す
 アンカータグのリンク先をGoogleに変更

<a>Googleへ</a>
<script>
    const link = doucment.querySelector("a");
    link.setAttribute("href", "https://google.com");
</script>

◇ データ属性

データ属性は、ユーザーが独自で値を保持するためのHTML属性である。
データ属性は、data-*のような形式で表される。
JavaScriptからデータ属性へアクセスするときはdatasetという特別なプロパティを通してアクセスする。

// データ属性の取得・変更
<div id="sum" data-base-val1="10" data-base-val2="20"></div>
<script>
    const sum = document.querySelector("#sum");
    const val1 = Number(sum.dataset.baseVal1); // データ属性値を取得 "-"はキャメルケースとして表現する。
    const val2 = Number(sum.dataset.baseVal2);
    sum.dataset.sumVal = val1 + val2; // データ属性の追加も可能
    sum.textContent = val1 + val2;
</script>

14.2.5 要素の位置と大きさを取得と変更

HTML/DOMでは、要素の大きさや位置を状況に合わせて、3種類の方法で取得できる。

・ 位置情報を保持するプロパティ

プロパティ 説明
clientLeft
clientTop
自要素の左ボーダーの幅(clientLeft)、上ボーダーの幅(clientTop)のpx数を数値で返す。読み取り専用
offsetLeft
offsetTop
offsetParent要素から自要素のボーダー(border)の外側(左:clientTop、上:offsetTop)までのpx数を整数値で返す。読み取り専用
scrollLeft
scrollTop
スクロール可能なコンテンツ領域の端からパディング(padding)の端(左:scrollLeft、上:scrollTop)までのpx数を整数で返す。値を変更したい場合は、変更したpx分のスクロールが行われる。

・ 大きさを保持するプロパティ

プロパティ 説明
clientWidth
clientHeight
要素のパディング(padding)までを含む矩形の横幅(clientWidth)、高さ(clientHeight)のpx数を整数値で取得する。スクロールバーは含まない。読み取り専用
offsetWidth
offsetHeight
要素のボーダー(border)までを含む矩形の横幅(offssetWidth)、高さ(offsetHeight)のpx数を整数値で取得する。スクロールバーがあるときはスクロールバーを含む。読み取り専用
scrollWidth
scrollHeight
画面表示されていないスクロール可能なコンテンツ領域を含む矩形の横幅(scrollWidth)、高さ(scrollHeight)のpx数を整数値で取得する。読み取り専用

14.2.6 位置や大きさを確認するためのメソッド

より詳細な要素の位置や大きさを確認するには、getBoundingClientRectメソッドを使う。

[構文] getBoundingClientRectの使い方
const domRect = element.getBoundingClientRect();

element: Elementオブジェクトを設定する。
domRect: 要素の位置と大きさを表すプロパティを格納したオブジェクト(DOMRect)を返す。

> プロパティを格納したオブジェクト(DOMRect)
    {
        left: ビューポートの左端から枠線(border)の左端までの距離,
        top: ビューポートの上端から枠線の上端までの距離,
        right: ビューポートの左端から枠線の右端までの距離,
        bottom: ビューポートの上端から枠線の下端までの距離,
        x: leftと同じ,
        y: topと同じ,
        width: 枠線とパディング(padding)を含めた横幅(offsetWidthと基本同じ),
        height: 枠線とパディング(padding)を含めた縦幅(offsetHieghtと基本同じ)
    }

14.3 スタイルの変更

画面上のスタイル(見栄え)の変更は、CSS(Cascading Style Sheets)と呼ぶ記法でHTMLタグにデザインを適用する。

14.3.1 CSSの基礎

CSSをHTML要素に適用するには、次の何れかの方法でスタイルを定義する。

  • style属性で指定する。(HTMLのstyle属性にCSS形式で値を追加する。)
    [構文]style属性の記法
    <div style="プロパティ: 値; プロパティ: 値"></div>
  • styleタグで指定する。(styleタグの中にCSSの記法で要素を指定する。)
    [構文]CSSの記法(セレクタの定義)
    セレクタ {
        プロパティ: ;
        プロパティ: ;
        ...
    }
    
    別のセレクタ {
        ...
    }

14.3.2 style属性の変更

[構文]style属性の変更
Elementオブジェクト.style.CSSプロパティ名 = 設定したい値;

Elementオブジェクト: スタイル(見栄え)を変更したい要素。
CSSプロパティ: CSSのプロパティ名()を設定する

() ハイフン(-)をはさむプロパティ名は、キャメルケースを使う。
() background-color  backgroundColor
・styleプロパティから文字色と背景色を変更
<div>2秒後に文字と背景の色が変わります。</div>
<script>
    setTimeout(() => {
        const div = document.querySelector("div");
        div.style.color = "red";
        div.style.backgroundColor = "blue";
    }, 2000);
<script>

14.3.3 classListを使ったスタイル変更

JavaScriptからclass属性の値を操作する場合は、ElementオブジェクトのclassListプロパティに格納されている、次のメソッドを使用する。

[構文] class属性の値を変更するメソッド

// class属性にクラス(className)を追加する。
Elementオブジェクト.classList.add("className");
<div class=""> ➡ 変更後 ➡ <div class="className">

// class属性からクラス(className)を削除する。
Elementオブジェクト.classList.remove("className");
<div class="className"> ➡ 変更後 ➡ <div class="">

// class属性のクラス(className)を付けはずしする。
Elementオブジェクト.classList.toggle("className"); 
<div class=""> ⇦ 呼び出しごとにクラスの付けはずし ➡ <div class="className">

// クラス(className)がclass属性内に存在するか確認する。
Elementオブジェクト.classList.contains("className"); // 存在する場合trueが返える。
・classListを使ったスタイルの変更
<div>2秒後に文字と背景の色が変わります。</div>
<style>
    .preparedClass {
        color: red;
        background-color: blue;
    }
</style>

<script>
    setTimeout(() => {
        const div = document.querySelector("div");
        div.classList.add("preparedClass");
    }, 2000);
</script>
⚠️ **GitHub.com Fallback** ⚠️