「HTML、CSSはそれなりに書けるようになったけど、JavaScriptはググってコピペしている」
「jQueryから知ったため純粋なJavaScriptの長い文法が何をしているのかイマイチ理解できない」
「オリジナルでアニメーションを作るときに自分で作ったものが何故か動かないことが多い」
本日はそんな方に読んでもらいたい、「JavaScriptでのHTML要素の操作方法の基本」を紹介していきます。
最近ではWeb制作でもJavaScriptを使ったアニメーションが多用されるため、Webデザイナーやマークアップエンジニアの方でもJavaScriptを知らないといけなくなっています。
もちろんググれば作り方は出てくるのですが、そもそもの基本構文の説明まではされていないものが結構あるため「何となく」で作ることになっている人が多いと思います。
とはいえ基本的な内容は一度知れば「そんなもんか」となるはずです。
最後に簡単なモーダルを作る演習もあるのでぜひ最後まで読んでください。
また動画もあるので必要に応じて使ってください。
JavaScriptでHTML要素を取得する方法
まずはHTML要素の「取得」から紹介していきます。
HTML要素の取得が他の内容の根幹を担っているので、JavaScriptに苦手意識がある方もこれだけは知っておいて欲しいです。
まず簡単なHTMLだけ用意しておきます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./script.js" defer></script>
</head>
<body>
<div class="div"></div>
</body>
</html>
上記のコードで<div class=”div”></div>という部分を取得したいときはこんな風に書きます。
const div = document.querySelector(".div");
console.log(div); // <div class="div"></div>と出力される
いきなりdocumentという見慣れない単語が出てきましたね。
こちら公式ドキュメントにも記載されているのですが、やや説明が難しく感じられるような書き方になっているのでチンプンカンプンになった方もいるのではないでしょうか。
https://developer.mozilla.org/ja/docs/Web/API/Document
こちらの説明は簡単に言い換えると「HTML要素の設計図にアクセスするためのもの」ということです。
documentを使うことでHTMLタグで構成された色んな要素にアプローチすることができます。
加えてdocumentには事前に便利な関数がたくさん用意されていて、それらの関数を使うことでHTML要素の操作が可能になっています。
先ほどのコードにある「querySelector」もdocumentに用意されている関数の一つで「引数に指定したセレクタに合う要素を取得」することを実現してくれます。
もう一度コードを見てみましょう。
const div = document.querySelector(".div");
console.log(div); // <div class="div"></div>と出力される
こちらのコードで「class名がdivになっているHTML要素を取得」という意味になっています。
取得したHTML要素は上記にあるように定数や変数に格納して、他の場所で使い回すことができます。
同じclass名が複数あったとき
ちなみにclass名は同じ名前のものが複数あるときがありますよね。
HTMLコードを少し書き換えてみます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./script.js" defer></script>
</head>
<body>
<ul class="items">
<li class="item">1</li>
<li class="item">2</li>
<li class="item">3</li>
</ul>
</body>
</html>
「item」というclass名の要素が3つありますよね。
こんな時にquerySelectorを使うとどうなるのか見てみます。
const li = document.querySelector(".item");
console.log(li); // <li class="item">1</li>が出力される
無事に要素が取得できましたが、困ったことに1つしか出力されませんでした。
こちら公式ドキュメントにも記載があるのですが、querySelectorは条件に合う最初の要素しか取得できないという特徴があります。
そこで違う関数で「querySelectorAll」というものが用意されています。
const li = document.querySelectorAll(".item");
console.log(li); // NodeList(3) [li.item, li.item, li.item]と出力される
配列で3つの要素が取得できています。
class名は同じ名前を何回も使えるが故にJavaScript側での扱いには少し注意しましょう。
こちら配列なので個別に使いたいときには繰り返し処理で取り出す必要があります。
const li = document.querySelectorAll(".item");
console.log(li);
const liLength = li.length;
for(let i = 0; i < liLength; i++){
console.log(li[i]);
}
// <li class="item">1</li><li class="item">2</li><li class="item">3</li>と出力される
「querySelectorAll」の結果は配列になっているので、配列の中身はインデックス番号で区別できます。
そのためインデックス番号で各それぞれの要素を出力することができるわけですね。
特定の要素のみ取得したいとき
同じclass名が複数あるときに、「特定の1つのみ」を取得したいときはどうすれば良いのでしょうか?
幾つか方法があるのですが初学者向けに一番簡単な方法として、HTML側でidを指定しておくものがあります。
あらかじめHTML側でidを指定しておきましょう。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./script.js" defer></script>
</head>
<body>
<ul class="items">
<li class="item" id="itemOne">1</li>
<li class="item">2</li>
<li class="item">3</li>
</ul>
</body>
</html>
classと違ってidは同じ名前を複数使うことができませんので、その特性を利用するわけです。
また「querySelector」でid名を指定するときは先頭が「#」になるので気をつけてください。
const li = document.querySelector("#itemOne");
console.log(li); // <li class="item" id="itemOne">1</li>と出力される
同じclass名が複数あったとしても、特定の1つの要素のみを取得することができましたね。
HTML、CSSでのコーディングだけだとidを指定する機会は無いので「idっていつ使うんだろう?」となったことがあるかもしれません。
JavaScriptではidをよく使うので覚えておきましょう。
親子関係の要素を取得する
下記のように親子関係になっているHTMLを取得するのも「querySelector」で実現できます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./script.js" defer></script>
</head>
<body>
<ul class="items">
<li class="item" id="itemOne">1</li>
<li class="item">2</li>
<li class="item">3</li>
</ul>
</body>
</html>
const ul = document.querySelector(".items");
console.log(ul);
親子関係をまとめて取得したいときは親のセレクタを指定するだけで大丈夫です。
また子の要素だけを取得したいときは以下のようにすれば取得できます。
const ul = document.querySelector(".items").children;
console.log(ul); // HTMLCollection(3) [li#itemOne.item, li.item, li.item, itemOne: li#itemOne.item]と出力される
こちら複数要素になるので配列になっています。
さらに細かく分解したいときは先ほどのように繰り返し処理などで取得できます。
配列とNodeListと配列風オブジェクトの違い
class名で複数の同じ名前があった場合に全てを取得する方法をquerySelectorAllで紹介しました。
実はもう一つgetElementsClassByNameというものを使っても同じようなことができるので追加で紹介です。
querySelectorAllとgetElementsClassByNameの違い
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./style.css" />
<script src="./script.js" defer></script>
</head>
<body>
<div class="cards">
<div id="card" class="card"></div>
<div class="card"></div>
<div class="card"></div>
<div class="card"></div>
</div>
</body>
</html>
const cardClass = document.getElementsByClassName('card');
console.log(cardClass);
querySelectorAllのときと同じような結果を得ることができました。
ブログ記事などを調べたときにはgetElementsClassByNameを使った方法がたくさん登場しますが、こちらの方法で取得する場合は少しだけ注意が必要です。
例えば取得した配列をもとに繰り返し処理をしたいとします。
const cardClass = document.getElementsByClassName('card');
// ここを修正
cardClass.forEach((card) => {
card.classList.add('active');
});
class=”card”となっているところを、class=”card active”にする文法です。
アニメーションの実装でよく見かける書き方かと思います。
しかしこちらはエラーになってしまいます、「forEachは使えません」というエラー文が表示されます。
先ほどの要素の取得結果をもう一度見てみるとわかるのですが、HTMLコレクションという名前の配列になっていますね。
こちらは「配列風オブジェクト」と呼ばれていてオブジェクトの一種になります。
ややこしいのですがHTMLコレクションというのは純粋な配列ではないため、forEachメソッドは使用できない仕様になっているためエラーになったんです。
そのため一番シンプルなfor文に書き換えてあげる必要があります。
const cardClass = document.getElementsByClassName('card');
// ここを修正
for (const card of cardClass) {
card.classList.add('active');
}
エラーもなくclass名にacitiveが追加されているのがわかります。
ちなみにquerySelectorAllを使った要素の取得をしていればforEachのままでも問題ありません。
// ここを修正
const cardClass = document.querySelectorAll('.card');
// ここを修正
cardClass.forEach((card) => {
card.classList.add('active');
});
コンソールで確認した時にパッと見で配列に見えてしまうのでquerySelectorAllでもgetElementsByClassNameも同じものに感じられるのですが使用できるメソッドが違うので注意しましょう。
querySelectorAllは静的なNodeListを返していることは注意
ちなみにquerySelectorAllが返しているのは静的なNodeListと呼ばれる配列の種類になります。
今の説明だとforEachメソッドが使えるので便利そうに見えるのですが、こちらも注意点がありますので共有しておきます。
NodeListについては次章で説明しますが動的なNodeListと静的なNodeListがあり、JavaScriptを使ったDOM操作で違いがあります。
例えば以下のようなHTMLがあるとします。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./style.css" />
<script
src="https://yubinbango.github.io/yubinbango/yubinbango.js"
charset="UTF-8"
></script>
<script src="./script.js" defer></script>
</head>
<body>
<div class="container">
<div class="box1 box">ボックス1です</div>
<div class="box2 box">
ボックス2です
<!-- 子要素です -->
<p>説明文です</p>
</div>
</div>
</body>
</html>
こちらJavaScript側で要素の取得をやってみます。
const container = document.querySelector('.container');
const box1 = document.querySelector('.box1');
const box2 = document.querySelector('.box2');
console.log(box1);
console.log(box2);
現状のHTMLの構造ではclass=”box”という要素が複数ありますので、全てに同じ処理をしたい場合にquerySelectorAllで取得すればよかったですね。
const container = document.querySelector('.container');
const box1 = document.querySelector('.box1');
const box2 = document.querySelector('.box2');
// ここを追加
const boxes = document.querySelectorAll('.box');
console.log(box1);
console.log(box2);
ここまでは特に難しいことはありません、この状態からJavaScript側で新しくHTMLタグを追加してみます。
const container = document.querySelector('.container');
const box1 = document.querySelector('.box1');
const box2 = document.querySelector('.box2');
const boxes = document.querySelectorAll('.box');
console.log(box1);
console.log(box2);
// ここから追加
const newEl = document.createElement('div');
newEl.classList.add('box');
container.appendChild(newEl);
空のdivタグを追加することができました。
またclass=”box”が3つに増えましたので、先ほど取得していた定数boxesには3つのclass=”box”の要素が格納されていることになります。
const container = document.querySelector('.container');
const box1 = document.querySelector('.box1');
const box2 = document.querySelector('.box2');
const boxes = document.querySelectorAll('.box');
console.log(box1);
console.log(box2);
// ここを追加
console.log(boxes);
const newEl = document.createElement('div');
newEl.classList.add('box');
container.appendChild(newEl);
// ここを追加
console.log(boxes);
しかしコンソールで新しい要素の追加の前後で定数boxesの中身が増えていませんね。
これがquerySelectorAllは静的なNodeListを返すという内容です。
公式ドキュメントにも記載があるのですが多くの初心者はご存知ないはずです。
静的なNodeListはページのリロードや要素の再取得なしでは要素が更新されないようになっています。
また最近ではページをリロードしないで画面の表示とDOM更新をすることがトレンドですので注意が必要です。
先ほど見てもらったように画面の表示(HTML構造)としては空のdivタグが増えていましたので、画面の表示するだけなら問題ないのですが、JavaScriptでDOM操作するには空のdivタグは追加されていないのと同じことになっているわけです。
前章でも紹介したgetElementsByClassNameだと配列風オブジェクトを返すので動作が違ってきます。
querySelectorAllの部分を修正してみましょう。
const container = document.querySelector('.container');
const box1 = document.querySelector('.box1');
const box2 = document.querySelector('.box2');
// ここを修正
const boxes = document.getElementsByClassName('box');
console.log(box1);
console.log(box2);
console.log(boxes);
const newEl = document.createElement('div');
newEl.classList.add('box');
container.appendChild(newEl);
console.log(boxes);
今度はdivタグが3つに増えていますね、配列風オブジェクトだと画面の表示(HTML構造)だけでなくDOMの更新もリアルタイムに行なってくれます。
どちらが正しいというより前章で紹介したNodeListと配列風オブジェクトの違いは意外と身近に影響していることを覚えておきましょう。
ちなみにquerySelectorAllの静的なNodeListで取得していたとして、DOMの更新をリアルタイムに実行したい場合は再度querySelectorAllを実行して要素の再取得をすればOKです。
const container = document.querySelector('.container');
const box1 = document.querySelector('.box1');
const box2 = document.querySelector('.box2');
// ここを修正
const boxes = document.querySelectorAll('.box');
console.log(box1);
console.log(box2);
console.log(boxes);
const newEl = document.createElement('div');
newEl.classList.add('box');
container.appendChild(newEl);
// ここを修正
const newBoxes = document.querySelectorAll('.box');
console.log(newBoxes);
これで新しく空のdivタグを追加した後にDOM側でもclass=”box”の要素が3つになっていることがわかります。
NodeListと配列の違い
それでは純粋な配列とは何なのでしょうか?
もう一つの例題としてquerySelectorAllで複数の要素を取得した場合を試しましょう。
実はquerySelectorAllで取得したものを配列だと思われるかもしれませんが、こちらは配列ではなく「NodeList」というものになります。
const lists = document.querySelectorAll(".list");
const arr = ["aaa", "bbb", "ccc"];
console.log("NodeList:", lists);
console.log("配列:", arr);
NodeListと配列が違うものであることはメソッドを使うとわかります。
例えば繰り返し処理で使うmapメソッドです。
こちらをそれぞれ使ってみるとNodeListではエラーになり使えないことが確認できます。
const lists = document.querySelectorAll(".list");
const arr = ["aaa", "bbb", "ccc"];
arr.map((a) => {
console.log(a);
});
lists.map((list) => {
console.log(list);
});
mapメソッドは配列のために用意されたメソッドであり、NodeListで使うとエラーになります。
ちなみに使えるメソッドはプロトタイプとも呼ばれてコンソールで確認できます。
const lists = document.querySelectorAll(".list");
const arr = ["aaa", "bbb", "ccc"];
console.log("NodeList:", lists);
console.log("配列:", arr);
配列の方は数えきれないくらいのメソッドが表示され、これら全てのメソッドを使うことができます。
一方でNodeListの方は配列と同じメソッドもありますが圧倒的に数が少ないですね。
またmapメソッドが存在していません。
前章でやったようにquerySelectorAllで取得して繰り返しをしたいときはforEachが使えます。
それはNodeListというものが配列ではないものの、forEachがプロトタイプに含まれているからです。
getElementsByClassNameでは配列風オブジェクトと言いましたが、こちらはforEachですらプロトタイプに含まれていませんので前章で紹介したようなことになったわけです。
querySelectorAll、getElementsByClassNameで取得したものは配列ではありません、必ず理解しておきましょう。
「正しく書いているはずなのにエラーになる」というときは、もしかしたら使えないメソッドを書いていることが原因かもしれません。
今自分が操作しようとしている要素は「配列」「NodeList」「配列風オブジェクト」のどれに当たるのかを意識できるようになるとエラーが少なくなるでしょう。
要素とNodeの違いとは?
いろんなメソッドやキーワードが出てきて混乱しているかもしれませんね。
1回で全てを理解できるわけではないので詰まったら都度復習していくことで理解が定着するはずです。
ここまで話してきて「要素とNodeって同じものじゃないの?」と思われている方がいると思いますので、要素とNodeの違いをお話します。
結論、要素はHTMLタグのことで、NodeはHTMLタグとテキスト、コメントなど全てのことを指します。
以下のようなHTMLがあったとします。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./style.css" />
<script
src="https://yubinbango.github.io/yubinbango/yubinbango.js"
charset="UTF-8"
></script>
<script src="./script.js" defer></script>
</head>
<body>
<div class="container">
<div class="box1 box">ボックス1です</div>
<div class="box2 box">
ボックス2です
<!-- 子要素です -->
<p>説明文です</p>
</div>
</div>
</body>
</html>
class=”box1″とclass=”box2″についてJavaScript側で取得しておきます。
const box1 = document.querySelector('.box1');
const box2 = document.querySelector('.box2');
console.log(box1);
console.log(box2);
HTMLではテキストがあったり入れ子でHTMLタグがあったりします。
JavaScriptでは親要素をparentElement、子要素をchildrenというプロパティ名で取得することができます。
const box1 = document.querySelector('.box1');
const box2 = document.querySelector('.box2');
console.log(box1);
console.log(box2);
// ここから追加
console.log('親要素:', box1.parentElement);
console.log('子要素:', box1.children);
console.log('親要素:', box2.parentElement);
console.log('子要素:', box2.children);
box1もbox2も結果はほとんど同じですが子要素であるchildrenプロパティのみ違います。
box1は「テキスト」しか無いため配列風オブジェクト(HTMLCollection)の中身が0になっていますね。
一方でbox2はpタグがありましたので、0番目に位置にpタグが格納されているのがわかります。
続いてNodeというもので親Nodeと子Nodeを取得してみましょう。
親NodeはparentNode、子NodeはchildNodesというプロパティ名で取得することができます。
const box1 = document.querySelector('.box1');
const box2 = document.querySelector('.box2');
console.log(box1);
console.log(box2);
// ここから修正
console.log('親Node:', box1.parentNode);
console.log('子Node:', box1.childNodes);
console.log('親Node:', box2.parentNode);
console.log('子Node:', box2.childNodes);
親Nodeについては同じ結界になりましたが、子Nodeについては違いが出ています。
まずbox1の方にもtextというものが1つだけですが表示されています。
先ほどbox1は空で今回は表示されたのは、要素はHTMLタグのことで、Nodeはテキストやコメントなど全てのことだからです。
またbox2についてはタグ、テキスト、コメントまで合計5つが表示されていますね。
コメントアウトはブラウザ上では表示されないですがNodeにすると取得することができるんですね、意外と知らない方が多いです。
要素とNodeの違いは同じようなものに見えますが、対象としているものが違うことがわかりましたね。
JavaScriptでHTML要素の作成をする方法
すでに書かれているHTMLコードから要素を取得することができたので、要素を新しく作る方法を紹介します。
まず下地に以下のようなHTMLがあるとします。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./script.js" defer></script>
</head>
<body>
<div class="container">
<div class="item">テスト</div>
</div>
</body>
</html>
「テスト」とい文字が表示されていますね。
まずdivタグを作成してみたいと思います。
const div = document.createElement("div");
console.log(div); // <div></div>と出力される
要素の取得でも登場したdocumentの「createElement」という関数で簡単にHTMLタグを作ることができます。
続いてタグに挟むテキストも作成してみます。
const div = document.createElement("div");
// こちらを追加
div.innerHTML = "テスト";
console.log(div); // <div>テスト</div>と出力される
HTMLタグにテキストを追加するには「innerHTML」という関数を使います。
これで立派なHTML要素が完成しましたね。
しかし現状だと「作成しただけ」なので、画面に表示してみます。
const div = document.createElement("div");
div.innerHTML = "新規作成";
// こちらを追加
document.body.appendChild(div);
文章が2つに増えていますね。
画面に出力する際には「どこに表示するのか」という情報が必要になり、今回はbodyタグにしたので「document.body」となっています。
続けて「appendChild(作成した要素)」と書くことで、bodyタグのなかに作成したdivタグが表示されたわけです。
実務ではbodyタグに表示させるケースは多くはなくて、「特定の要素に入れ子で作成する」といった具合になります。
やり方は簡単で「親にしたい要素を取得する」ことさえ出来ればOKです。
こちら冒頭で紹介した「要素の取得」になりますよね。
現状は以下のようなHTMLの構成になっているとして、<div class=”container”></div>のなかに新規作成してみます。
画面には何も表示されていない状況です。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./script.js" defer></script>
</head>
<body>
<div class="container"></div>
</body>
</html>
// ①親にする要素を取得
const container = document.querySelector(".container");
// ②HTMLタグを作成
const el = document.createElement("div");
// ③class名を作成
el.classList.add("test");
// ④テキストを作成
el.innerHTML = "テスト";
// ⑤①の子として表示させる
container.appendChild(el);
順番に説明していきます。
①まず作成する場所(親にする要素)を決めて取得します。こちらは冒頭に登場したquerySelectorを使っています。
②createElementでHTMLタグを作成します。こちらも先ほど紹介したcreateElementを使っています。
③②で作成したタグにclass名を作成しています。初めて紹介するのですがclassList.add(クラス名)とするとclass名を追加することができます。
④表示するテキストを作成します。こちらも先ほど紹介したinnerHTMLを使っています。
⑤①の子として新規作成したいのでappendChildを使っています。
画面にテキストが表示されていますね。
ブラウザの検証ツールでHTMLの構成も確認しておきましょう。
<div class=”container”></div>の子として入れ子状態でHTMLの新規作成が実現できているのが分かります。
ちなみに今やった内容は以下のような書き方でも実現できます。
const container = document.querySelector(".container");
const el = `<div class="test">テスト</div>`;
container.insertAdjacentHTML("afterbegin",el);
新規作成する要素は「“」を使って完成形を書くことでも大丈夫です。
先ほど紹介した方法と違って「タグ」「class名」「テキスト」を1行でまとめられるので楽ですよね。
insertAdjacentHTMLという関数が登場しましたが、第一引数に「場所」で第二引数に「作成した要素」を指定すれば、appendChildのように画面に表示することができます。
第一引数は公式ドキュメントにも記載ありますが、すでにキーワードが決められているので随時調べて選択してもらうと良いでしょう。
上記のコードでは「afterbegin」を使用しました。
https://developer.mozilla.org/ja/docs/Web/API/Element/insertAdjacentHTML
複製するだけのパターン
ちなみに新規作成と違って、「既にあるものを複製したい」という場面もあります。
下記のHTMLで<div class=”item”>テスト</div>の部分を複製してみたいと思います。
const copy = document.querySelector(".item").cloneNode(true);
document.querySelector(".container").appendChild(copy);
テストという文章が2つに増えていますね。
複製するときは「複製する対象を取得」する必要があり、そちらは冒頭で説明した「querySelector」で実現できます。
加えて「cloneNode(true)」と書くことで複製することができます。
JavaScriptでHTML要素を削除する方法
続いてHTML要素の削除を紹介します。
まずHTMLを以下のように想定します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./script.js" defer></script>
</head>
<body>
<ul class="items">
<li class="item">1</li>
<li class="item">2</li>
<li class="item">3</li>
</ul>
</body>
</html>
リストが表示されていますね。
上記のHTMLにおいて、「class=”item”」となっている要素を削除したいと思います。
①削除する場所(削除対象の親要素)を取得
const items = document.querySelector(".items");
②削除する要素を取得
const item = document.querySelector(".item");
③削除を実行
items.removeChild(item);
リストが1つ消えていますね、順番に説明していきます。
①まず削除したい要素がある場所を指定する必要があります。とはいえ親要素になることがほとんどでしょう。要素の取得はquerySelectorで行います。
②続いて削除したい要素自身をquerySelectorで取得します。
③①に続けて「removeChild(削除する要素)」と書くと削除が実行されます。
ちなみに既に気付かれた方がいるかもしれませんが、「class=”item”」という要素は3つありました。
こちら冒頭で説明したようにquerySelectorで取得する際に、同じ名前が複数あった場合には「最初の要素のみ」を取得します。
そのため上記のコードでは1つしか削除されなかったんですね。
同じセレクタ名の要素を全て削除したいとき
では同じセレクタ名のものを全て削除したいときはどうすればいいのか、こちら先ほど紹介した繰り返し処理を使えば実現できます。
const items = document.querySelector(".items");
const itemAll = document.querySelectorAll(".item");
const itemLength = itemAll.length;
for(let i = 0; i < itemLength; i++){
items.removeChild(itemAll[i]);
}
同じセレクタ名のものを全て取得するにはquerySelectorAllを使えば良かったですよね。
こちら配列で取得されるので、繰り返し処理をしながら各要素はインデックス番号で指定できます。
上記のコードを実行することで画面から文字が無くなります。
同じセレクタ名があった際に特定の要素のみ削除したいとき
さらに同じセレクタ名で特定の要素のみ削除したいときも解説します。
冒頭で紹介した「idを指定する」という方法でも良いのですが、せっかくなので別の方法を紹介します。
HTMLは引き続き以下のような状態を使用します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./script.js" defer></script>
</head>
<body>
<ul class="items">
<li class="item">1</li>
<li class="item">2</li>
<li class="item">3</li>
</ul>
</body>
</html>
上記のコードで3番目の<li class=”item”>3</li>のみを削除したいとした場合、このように書くことでも特定の要素を指定したことになります。
const items = document.querySelector(".items");
const itemLast = document.querySelector(".item:last-child");
items.removeChild(itemLast);
3番目の要素のみ削除されていますね。
要素の取得に使用するquerySelectorの引数ではCSSの疑似クラスを使用することができます。
・最初の要素:first-child
・最後の要素:last-child
・2番目の要素:nth-child(2)
といった具合にCSSが分かる人なら大丈夫だと思います。
モーダルの実装方法
最後にここまで紹介した関数を組み合わせることで簡単なモーダルが作れるので紹介します。
慣れないうちはアニメーションは何だか難しい計算が必要に見えるのですが、結局のところ基本的な関数の組み合わせで再現できる場合が多いです。
まずHTMLから作っていきます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./style.css">
<script src="./script.js" defer></script>
</head>
<body>
<div class="container">
<button class="btn">詳細</button>
</div>
</body>
</html>
「詳細」と書かれたボタンが表示されていますね、こちらのボタンをクリックしたらモーダルが表示されることを目指します。
// ①ボタンを取得
const btn = document.querySelector(".btn");
// ②モーダルの親要素を作成
const modal = document.createElement("div");
modal.classList.add("modal");
// ③モーダルの子要素を作成
const inner = document.createElement("div");
inner.classList.add("inner");
// ④作成した②③を親子関係で作成
modal.appendChild(inner);
// ⑤取得した①のボタンがクリックされたときに⑤を表示するイベント
btn.addEventListener("click", () => {
document.body.appendChild(modal);
})
// モーダルの一部をクリックしたら画面から削除するイベント
inner.addEventListener("click", () => {
document.body.removeChild(modal);
})
想定している動きとしては、「ボタンをクリック→モーダルの表示→モーダルをクリック→モーダルの非表示」という内容です。
ボタンについてはすでにHTMLで作成済なのでquerySelectorで要素の取得だけ事前に実行しておきます。
モーダルについては新規作成になるのでcreateElementで作成します。
今回は細かく作り込みませんが実務ではモーダルのなかにテキストやボタン、画像を表示するはずなので中身が親子関係になるようなモーダルを作成しています。
// ①ボタンを取得
const btn = document.querySelector(".btn");
// ②モーダルの親要素を作成
const modal = document.createElement("div");
modal.classList.add("modal");
// ③モーダルの子要素を作成
const inner = document.createElement("div");
inner.classList.add("inner");
// ④作成した②③を親子関係で作成
modal.appendChild(inner);
~省略~
あとはaddEventListenerのイベント機能を使うだけです。
モーダルの表示は「ボタンのクリック」がきっかけになるので以下のような形になります。
~省略~
// ⑤取得した①のボタンがクリックされたときに⑤を表示するイベント
btn.addEventListener("click", () => {
document.body.appendChild(modal);
})
~省略~
モーダルは画面を覆い被さる形になるので、非表示の機能も必要です。
非表示といっても「要素の削除」をすれば良いはずですので、以下のような形で実現できます。
~省略~
inner.addEventListener("click", () => {
document.body.removeChild(modal);
})
これにて完成なのですが、モーダルと分かるようにCSSで装飾しておきましょう。
.modal {
width: 100%;
height: 50%;
position: absolute;
top: 0;
left: 0;
}
.modal .inner{
width: 100%;
height: 100%;
background: rgba(0,0,255,0.4) ;
}
簡単な見た目ですがモーダルを作成することができました。
意外と単純な仕組みですよね、こんな感じでHTML要素の操作の基本が分かれば色んなものを作れるので一度しっかり理解しておくことをおススメします。
また理解をさらに深めるために以下の書籍もありますのでぜひ読破してみてください。