JavaScriptによるDOM操作 入門
今回は、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からエンジニアになるためのノウハウをブログで発信しています。
また、YouTubeでの動画解説も始めました。
YouTubeのvideoIDが不正ですインスタの発信も細々とやっています。
興味がある方は、ぜひリンクをクリックして確認してみてください!
Discussion