14 DOM - iruma-tea/dokushujs GitHub Wiki
JavaScriptからHTMLへの入出力はDOM(Document Object Model)というAPIを通して行う。
DOMは、DOMインタフェースやDOM APIとも呼ばれる。
JavaScriptのソースコード中では直接HTMLを扱うことができないため、DOMインタフェースを持つDOMオブジェクトを通してHTMLの情報を扱う。
Documentオブジェクトには、HTMLの構造がDOMオブジェクトに変換された状態で、ツリー構造で格納される。
DOMツリーを構成する個々のオブジェクトは**Node(ノード)と呼ばれる。
Nodeには、テキストやHTMLコメント、HTMLタグなどの種別がある。
Nodeの中でもHTMLタグのみを表す場合にElement(エレメント)**と呼ぶ。Elementとは、Nodeの種別がElementタイプ(HTML要素)のものを指す。
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を含む配列風オブジェクトを返す |
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>
現代のブラウザでは、**セレクタ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>
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>
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>
- 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コードが実行される。
DOMインタフェースを通して、HTML要素を取得・変更する
HTMLタグで囲まれた文字列や子要素を取得・変更するときには、ElementオブジェクトのinnerHTMLやtextContentにアクセスする。
プロパティ | 説明 |
---|---|
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>
要素の作成方法
- document.createElementメソッドを使う
- ダミーの要素のinnerHTMLに作成したいHTMLを挿入する
- templateタグを使う
document.createElementメソッドを使うと、DOMツリー上にないElementオブジェクトを新しく作成できる。
・ createElementを使った要素の作成
<body>
<!--新しい要素をここに追加したい-->
<script>
const newDiv = document.createElement("div");
newDiv.textContent = "Hello World";
document.body.prepend(newDiv);
</script>
</body>
<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>
<body>
<!--新しい要素をここに追加したい-->
<template id="tmp1">
<span>テンプレートHTMLを定義</span>
</tempalte>
<script>
const tmp1 = document.querySelector("#tmp1");
const targetNewElement = tmp1.content;
document.body.prepend(targetNewElement);
</script>
</body>
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> |
要素の属性の取得・変更(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>
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数を整数値で取得する。読み取り専用 |
より詳細な要素の位置や大きさを確認するには、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と基本同じ)
}
画面上のスタイル(見栄え)の変更は、CSS(Cascading Style Sheets)と呼ぶ記法でHTMLタグにデザインを適用する。
CSSをHTML要素に適用するには、次の何れかの方法でスタイルを定義する。
- style属性で指定する。(HTMLのstyle属性にCSS形式で値を追加する。)
[構文]style属性の記法 <div style="プロパティ: 値; プロパティ: 値"></div>
- styleタグで指定する。(styleタグの中にCSSの記法で要素を指定する。)
[構文]CSSの記法(セレクタの定義) セレクタ { プロパティ: 値; プロパティ: 値; ... } 別のセレクタ { ... }
[構文]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>
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>