😎

JavaScriptによるDOM操作 入門

2023/02/10に公開

今回は、JavaScriptでDOM操作をする方法を解説していきます。

JavaScriptの主要な役割は、DOM操作だと言っても過言ではないです。

なので、ぜひこの機会に学んでいきましょう。

DOM(document object model)とは?

まず、そもそもDOMとは何なのかということを解説していきます。

まず、ブラウザはHTMLを解析し、DOM作成して画面を描画しています。

そして、DOMとはHTMLの情報をプログラムから扱いやすい形にして、メモリに保存したものを言います。

例えば、HTMLファイルを書き換えるという作業はとても大変ですが、DOMを書き換えるという操作は簡単にできるようになっています。

なので、画面を書き換えたい場合は、JavaScriptでDOMを書き換えることになります。

そして、JavaScriptにおいてwindowオブジェクトにあるdocumentというオブジェクトを使うことで、DOMにアクセスすることができます。

まずはこのことを理解して、読み進めていってください。

html,Head,body

次に、具体的なDOMの構成要素と、取得方法などについて解説していきます。

まずは、HTMLの主要な要素の取得方法を紹介します。

具体的なコードは以下の通りです。

// html要素を取得できます
const html = document.documentElement;
// head要素を取得できます
const head = document.head;
// body要素を取得できます
const body = document.body;

このように、冒頭で述べた通り、documentオブジェクトを通してDOMにアクセスできます。

nodeの取得

次に、nodeの取得方法を解説していきます。

nodeとは、DOMを構成する1つの要素を表します。

取得方法は、コードで見ると次の通りです。

// 子供のnodeを配列のような形式で取得
html.childNodes;
// 最初の子nodeを取得
body.firstChild;
// 最後の子nodeを取得
body.lastChild;
// 同じ階層の次のnodeを取得
head.nextSibling;
// 同じ階層の前のnodeを取得
html.previousSibling;
// 親のnodeを取得
body.parentNode;

elementの取得

次に、elementの取得方法を解説していきます。

コードで見ると次の通りです。

// 子供のelementを配列のような形式で取得
html.children;
// 最初の子elementを取得
body.firstElementChild;
// 最後の子elementを取得
body.lastElementChild;
// 同じ階層の次のelementを取得
head.nextElementSibling;
// 同じ階層の前のelementを取得
body.previousElementSibling;
// 親のelementを取得
body.parentElement;

ちなみに、elementとはnodeの種類のことです。

例えば、h1,div,ulなどですね。

言葉の整理

まず基本的な基礎知識を学んでところで、次に言葉の整理をしていきます。

例えば、nodeやelementという言葉を使いましたが、これは先ほども説明したように意味が違います。

また、nodeやelement、documentではアクセスできるプロパティやメソッドが違ってきます。

他にも、テキストnodeも少し特徴が違います。

例えば、先ほど紹介したpreviousElementSiblingは、テキストnodeには使用することができません。

このことを理解していると、「なんでこれここで使えないんだ、、?」といった疑問もなくなるかと思います。

ちなみに、これらは中身を継承している場合が多いです。

例えば、HTMLInputElementはhtmlElementを継承しており、htmlElementはelementを継承しており、elementはnodeを継承しており、nodeはEventTargetを継承しています。

なので、nodeで使えるプロパティやメソッドは、elementでも使用することができます。

このことも一応覚えておきましょう。

特定のelementの取得

次は、特定のelementの取得方法を解説していきます。

こちらもコードを見た方が早いので次の通りです。

// cssセレクタを指定して一致する最初の要素を取得
html.querySelector("div");
// cssセレクタを指定して一致する全ての要素を取得
html.querySelectorAll("div");
// cssセレクタを指定して祖先を遡って一番近くにあるものを取得
body.closest("html");
// elementを指定して一致するかを返す
body.matches("body");
// nodeを指定して一致するかを返す
html.lastChild.contains(html.lastChild);

ちなみに、querySelectorは比較的新しいメソッドになります。

次に古いメソッドも紹介しますが、基本的にquerySelectorで代替可能です。

// id名をを指定して一致する最初の要素を取得
document.getElementById("app");
// name属性を指定して一致する全ての要素を取得
document.getElementsByName("title");
// tag名を指定して一致する全ての要素を取得
document.getElementsByTagName("div");
// class名を指定して一致する全ての要素を取得
document.getElementsByClassName("container");

DOMの変更

次に、DOMの変更方法を解説していきます。

UPDATE

まずは、DOMの更新方法を紹介します。

コードで説明すると次のようになります。

// DOMの中身を 全て書き換える
body.innerHTML = "<h1>Hello !</h1>";
// DOMの中身に追記する
body.insertAdjacentHTML("afterbegin", "<h1>Hello2 !</h1>");
// textの中身を 全て書き換える
body.innerText = "Hello!";
// textの中身に追記する
body.insertAdjacentText("afterbegin", "Hello2 !");

ちなみに、insertAdjacentHTMLとinsertAdjacentTextは、第一引数に挿入する場所を選べます。

今回使ってるafterbeginの他にも、3種類ほどあります。

また、ユーザーから受け取ったデータを表示する際にinnerHTMLなどを使ってしまうと、XSSなどの脆弱性に繋がるので、そういった場合はinnerTextを使うようにしましょう。

CREATE

次に、DOMの作成方法を解説していきます。

まず、createElementでDOMを作成することができます。

const div = document.createElement("div");

そして、次の4つのメソッドでDOMに追加することができます。

メソッド名 説明
append 1つ下の階層の一番後ろ
prepend 1つ下の階層の一番前
before 同階層の一番前
after 同階層の一番後ろ
const div = document.createElement("div");
div.innerText = "hello"
const body = document.body;
body.append(div)

DELETE

次にDOMの削除方法を解説していきます。

const div = document.createElement("div");
div.innerText = "hello";
const h1 = document.createElement("h1");
h1.innerText = "hello";
body.before(div);
// 指定したnodeを子要素も含めて削除
div.remove();
// 指定したnodeを削除して他のnodeと入れ替える
div.replaceWith(h1);

これらのメソッドを使ってDOMを削除することができます。

nodeの取得方法

次にnodeの情報の取得方法を解説していきます。

ザックリと説明すると、次の通りです。

const h1Title = document.getElementById('title')
// 1 仕様書で決められたnodeの種類ごとの数値を返す
console.log(h1Title.nodeType)
// H1 nodeの種類を返す
console.log(h1Title.nodeName)
// H1 nodeのタグ名を返す
console.log(h1Title.tagName)
// null 仕様書で決められたnodeの種類に対する値を返す
console.log(h1Title.nodeValue)

ちなみに、tagNameとnodeNameはどう違うんだと思うかもですが、例えばnodeNameはコメントノードの場合も情報を取得することができます。

なので、適宜使い分ける必要があります。

属性の取得・変更方法

次に、属性の取得・変更方法を解説していきます。

まず、2種類ほど方法を紹介します。

<h1 id="title" hoge="hoge">Hello!!</h1>

取得

const h1Title = document.getElementById('title')
// title
console.log(h1Title.id)
// undefined 適当な属性値は取得不可
console.log(h1Title.hoge)
// {0: id, 1: hoge, id: id, hoge: hoge, length: 2} 全ての属性情報を取得
console.log(h1Title.attributes)

変更

// title
console.log(h1Title.id)
h1Title.id = "test"
// test
console.log(h1Title.id)
// hoge
console.log(h1Title.attributes.hoge.value)
h1Title.attributes.hoge.value = "test"
// test
console.log(h1Title.attributes.hoge.value)

このように、直接属性値を書き換えることもできますし、attributesから属性のvalueを書き換えることで変更させることもできます。

ちなみに、data-*から始めた属性値は、datasetというプロパティで取得可能です。

<h1 id="title" data-hoge="bar">Hello!!</h1>
const h1Title = document.getElementById('title')
// DOMStringMap {hoge: 'bar'}
console.log(h1Title.dataset)

その他にも、いくつか属性値を操作できるメソッドがあるので紹介します。

const h1Title = document.getElementById('title')

// title 属性値の取得
console.log(h1Title.getAttribute('id'))
// 属性値の追加
h1Title.setAttribute('title','hello')
// true 属性値の存在確認
console.log(h1Title.hasAttribute('title'))
// 属性値の削除
h1Title.removeAttribute('title')
// false
console.log(h1Title.hasAttribute('title'))

結局どれ使えば良いの?と思うかもですが、最後に紹介したこれらのメソッドを使うのが一般的かと思います。

スタイル情報の取得方法

次に、スタイル情報の取得方法を解説していきます。

CSSにもDOMと同じように、CSSOMというものが存在します。

そして、それの取得方法は次の通りです。

document.styleSheets

ただ、こちらを操作するのは一般的ではないので、別の方法を紹介します。

まず、先ほどの考え方を応用して、属性値としてのstyleを取得することができます。

const h1Title = document.getElementById('title')

// blue
console.log(h1Title.style.color)
// 値の変更
h1Title.style.color = 'red'
// red
console.log(h1Title.style.color)

けれど、この書き方だと、style属性とした値しか取得できません。

つまり、cssを使って適用させた値は取得できないです。

なので、基本的スタイル属性を設定せずに、クラスのみでスタイルを操作するのが一般的です。

// title hoge スペースで区切った値を返す
console.log(h1Title.className)
// DOMTokenList(2) ['title', 'hoge', value: 'title hoge'] 配列のような形式で返す
console.log(h1Title.classList)

また、次のようにclassListには様々な便利なメソッドが用意されています。

// クラスを追加する
h1Title.classList.add('bar')
// クラスがある場合はを削除、ない場合は追加する
h1Title.classList.toggle('bar')
// クラスを削除する
h1Title.classList.remove('bar')
// クラスがあるか判定する
h1Title.classList.contains('bar')

要素の幅と場所

次に要素の幅と場所の取得方法を解説していきます。

ブラウザや要素の幅の取得

まずディスプレイの幅は以下の方法で取得できます。

const h1 = window.screen.height;
const w1 = window.screen.width;

けれど、これは飽くまでディスプレイの幅なので、ブラウザの幅とは異なります。

なので、ブラウザの幅は次の方法で取得できます。

const h1 = window.outerHeight;
const h2 = window.innerHeight;
const w1= window.outerWidth;
const w2 = window.innerWidth;

outerHeightはブックマークなどの幅も含むので、innerHeightを使うの一般的です。

次に、要素ごとの幅の取得方法を解説していきます。

・clientHeight:コンテンツ領域とpaddingまでのサイズ
・offsetHeight:コンテンツ領域とpadding、borderまでのサイズ

あと、scrollHeightというのもありますが、基本的にclientHeightと同じなのでそこまで意識しなくてOKです。

また、documentElementに使った場合は少し挙動が異なるので、そのような使い方はなるべくしない方が無難です。

要素の場所とスクロール

次に、要素の場所の取得とスクロールの方法について解説していきます。

まず、getBoundingClientRectで要素の位置や幅などを取得できます。

次に、スクロールの方法を解説します。

まず、scrollTopでどのくらいスクロールされたかを取得できます。

また、scrollToで特定の位置にスクロールさせることができます。

さらに、scrollByで今の位置からの相対的な位置を指定してスクロールさせることが可能です。

また、scrollintoViewというメソッドを使うことで、要素が見えるようになるまでスクロールさせることが可能です。

余談ですが、bodyかhtml要素にoverflow:hiddenを設定した場合は、スクロール自体ができなくなるので頭の片隅に置いておいてください。

liveとstatic

最後に、いくつか知っておくべきDOMの知識を解説します。

こちらの記事で、いろいろなオブジェクトを紹介しましたが、こちらは2つの種類に分けることができます。

それは、LiveとStaticです。

Live(動的)なオブジェクトとは、取得した要素に変更があった場合、その変更が代入先の変数にも適用されるオブジェクトです。

HTMLCollectionオブジェクトなどが Liveなオブジェクトに該当します。

そして、非Live(静的)なオブジェクトとは、取得した後に取得元の要素に変更があっても、その変更が代入先の変数に適用されないオブジェクトです。

querySelectorAllメソッドのNodeListオブジェクトなどが該当します。

なので、NodeListを変更して、再びquerySelectorAllメソッドを使用しても、変更前の情報しか取得することはできません。

一応このことも知っておきましょう。

また、MutationObserverという機能があり、こちらはDOMの変更を監視し、変更に応じてコールバック関数を実行することができます。

こちらもたまに使うことがあるものなので、一応頭の片隅に置いておきましょう。

まとめ

今回は、JavaScriptによるDOM操作方法について解説しました。

ぜひ、こちらの記事を参考にDOM操作の知識を深めてください。

宣伝

0からエンジニアになるためのノウハウをブログで発信しています。
https://hinoshin-blog.com/

また、YouTubeでの動画解説も始めました。
YouTubeのvideoIDが不正ですhttps://www.youtube.com/channel/UCqaBUPxazAcXaGSNbky1y4g

インスタの発信も細々とやっています。
https://www.instagram.com/hinoshin_enginner/

興味がある方は、ぜひリンクをクリックして確認してみてください!

Discussion