こんにちは。ECF Tech担当
Michiharu.Tです。
JavaScript中級編 第2回をお送りしたいと思います。
今回からフロントエンド開発に必要なjavascriptの基礎知識を学びます。まずはその土台ともいえるDOMについて学習します。
DOMの考え方や基本的な操作方法をお伝えできればと思っています。
どうぞ、よろしくお願いいたします。
本連載の章立ての一覧については下記のリンクから確認できます。
2-1 DOMの概要
DOM(Document Object Model) は、HTML文書をJavaScriptなどのオブジェクトとして表現するための仕様です。タグなどをオブジェクトとして表現することで、javascriptなどのプログラム言語で操作できるようにします。
HTML文書は下図(左)のように入れ子構造になっています。htmlタグ内にbodyタグとheadタグがあり、さらにbodyタグ内にh1タグやimgタグがある。といった具合です。DOMはこの入れ子構造を木構造として表現します。これをDOMツリーと呼びます。下図にHTML文書とDOMツリーの関連を示します。
DOMではHTMLの各タグをオブジェクトとして表現します。また、あるオブジェクトから見て自身の上にある要素を親要素、下にある要素を子要素と呼びます。図の例ですと、h1タグの直接の親要素はbodyタグとなります。
DOMではこれらのオブジェクトがどのようなプロパティやメソッドを持つべきかといったことが定義されています。私たちはこれらプロパティやメソッドを活用し、javascriptを使ってWebページを操作するプログラムを書くことになります。
2-2 土台となるオブジェクト
DOMではブラウザのウィンドウを、下図のようなオブジェクトの構成とみなしています。
Windowオブジェクト
ブラウザのウィンドウにあたるオブジェクトです。タブブラウザでは1つのタブに該当します。図で示すように、各オブジェクトを内部に持ちます。window
という変数で扱うことができます。
Documentオブジェクト
HTML文書そのものにあたるオブジェクトです。HTMLの各タグを要素として取得する場合は、このオブジェクトのメソッドを利用します。Documentオブジェクトは、Windowオブジェクトがdocument
というプロパティで保持していますが、Windowオブジェクトを指すwindow
は省略可能なため、通常はdocument
だけを使って表します。つまり、次の式はtrueとなります。
window.document === document
Locationオブジェクト
URL情報にあたるオブジェクトです。Windowオブジェクトがlocation
というプロパティで持っています。たとえば次のように記述することで、Webページを遷移できます。
location.href = "https://tech.e3factory.com/";
Historyオブジェクト
ブラウザの履歴を表すオブジェクトです。Windowオブジェクトがhistory
というプロパティで持っています。たとえば次のように記述することで、ひとつ前に閲覧したWebページに戻ることができます。
history.back();
2-3 要素の取得
次にDOMを使ったプログラミングの基礎となる要素の取得方法を確認します。様々な方法をプログラム例を通してご紹介します。まずは土台となるHTMLを示します。
プログラム(ch02_01.html)
<!DOCTYPE html> <html> <head id="header"> <meta charset="UTF-8"> <title>サンプル2-1</title> </head> <body> <h3 class="light">ごあいさつ</h3> <p id="explain">あいさつをしてみましょう。</p> <h4 class="light">日本語</h4> <ul> <li>おはよう</li> <li>こんにちは</li> <li>こんばんは</li> </ul> <button id="send" onclick="clicked()">送信</button> <script src="ch02_04.js"></script> </body> </html>
実行結果
※scriptタグのxxxxxx.js
の部分は、以降のjavascriptのプログラム例に記載のファイル名に適宜変更してご確認ください。
2-3-1 querySelector
代表的なタグ要素の取得方法はquerySelectorメソッドを使用する方法です。CSSのセレクタの要領で要素を取得できます。
プログラム例(ch02_01.js)
let p = document.querySelector('#explain'); console.log(p.innerHTML);
実行結果(コンソール出力)
あいさつをしてみましょう。
id属性の値を指定して要素を取得する場合は、引数に#ID名
と指定することで取得できます。
querySelectorを使った要素の取得パターンをいくつか示します。
タグ名指定
let p = document.querySelector('h4'); console.log(p.innerHTML);
h4
のタグ名で直接指定しています。
class属性指定
let p = document.querySelector('.light'); console.log(p.innerHTML);
.class名
でclass属性の値を指定して要素を取得できます。class属性がlight
の要素が複数ありますが、このような場合は最初の要素が返されます。
2-3-2 子要素の取得
ある要素からその子要素をまとめて取得することができます。プログラム例を示します。
プログラム例(ch02_02.js)
let ul = document.querySelector('#greeting'); for(let elem of ul.children){ console.log(elem.innerHTML); }
実行結果(コンソール出力)
おはよう こんにちは こんばんは
ある要素の子要素はchildrenプロパティで参照できます。childrenプロパティは配列になっており、子要素をすべて含みます。この例では、ID属性がgreeting
になっている要素(ul要素)を取得しています。配列の各要素は、for...of構文で変数elem
に取り出しています。elemに入るのはulタグ内の各li要素です。よって、各あいさつ文がコンソールに表示されます。
2-3-3 親要素の取得
ある要素からその親要素を取得することができます。プログラム例を示します。
プログラム例(ch02_03.js)
let li = document.querySelector('li'); let parent = li.parentElement; console.log(parent.tagName);
実行結果(コンソール出力)
UL
- querySelectorでliタグ要素を取得しています。オブジェクトは変数
button
に代入されます。 - parentElementプロパティを使って、親要素を取得します。親要素は変数
parent
に代入されます。 - 親要素のタグ名をtagNameプロパティを使って表示します。「UL」がコンソールに表示されます。
2-3-4 子要素の追加
子要素を追加してみましょう。リストなどでよく用いられます。プログラム例を示します。
プログラム例(ch02_04.js)
function clicked(){ let ul = document.querySelector('ul'); let item = document.createElement("li"); item.textContent = "おやすみ"; ul.appendChild(item); }
実行結果
- document.createElementメソッドを使ってliタグを作成しています。HTMLで表現すると
<li></li>
が作成されるイメージです。 - 作成したliタグに、textContentタグを使って「おやすみ」の文字列を追加します。HTMLのイメージは
<li>おやすみ</li>
となります。 - 最後にappendChildメソッドでulの子要素として追加します。要素は末尾に追加されることになります。
2-3-5 子要素の削除
プログラム例(ch02_04.js)
子要素を削除してみましょう。こちらもリストなどでよく用いられます。プログラム例を示します。
function clicked(){ let ul = document.querySelector('ul'); ul.removeChild(ul.children[1]); }
実行結果
- 2-3-2のプログラムの応用です。子要素はchildrenプロパティで取得できます。
- removeChildメソッドは引数で指定した要素を削除します。ulの子要素はchildrenプロパティで取得できます。childrenは配列なので、(0から数えて)1番目の要素を削除しています。
要素の操作方法は他にも様々なものがあります。必要に応じてインターネットを検索し、利用できるように練習をしてみましょう。
2-4 イベント
Webページ上では、下のような様々な操作が発生します。
- ボタンをクリックした
- 画面をスクロールした
- リンクの上にカーソルを乗せた
これらはイベントと呼ばれています。フロントエンド開発では、これらのイベントをきっかけに何かしらの処理を行う。というプログラムを書くための知識が必須です。
2-4-1 イベント処理の基本
イベントに応じた処理をするためにはイベントハンドラを要素に登録する必要があります。イベントハンドラは、イベント発生時に動作する関数です。簡単なプログラム例でイベントハンドラを登録し、動作させる方法を確認しましょう。
プログラム例(ch02/event.html)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>イベントテスト</title> </head> <body> <button>あいさつ</button> <script src="event.js"></script> </body> </html>
プログラム例(ch02/event.js)
function clicked(){ console.log('Hello!'); } const button = document.querySelector('button'); button.addEventListener('click',clicked);
event.htmlをブラウザで開いてボタンをクリックしてみましょう。クリックするたびにコンソールに「Hello!」が出力されます。
addEventListener
イベントハンドラの登録はaddEventListener
メソッドを使って行います。addEventListenerの基本的な使い方は次のようになります。
addEventListener(イベントの種類,イベント発生時に実行される関数)
最初の引数はイベントの種類です。これにはあらかじめ用意されたものが数多くあります。その一部をご紹介します。
種類 | 説明 |
---|---|
load | 要素の読込時 |
click | 要素のクリック時 |
change | 要素の変更時 |
mouseover | 要素の上にカーソルが乗った時 |
mouseout | 要素の上からカーソルがはずれた時 |
今回は要素のクリック時に動作するclick
を使用しています。
第2引数ではイベント発生時に実行される関数を記述します。今回登録した関数は、event.jsに定義されているclicked関数です。console.log("Hello!");
を実行するだけのものです。
2-2までで使っていたonclick属性を使った方法は、実利用では推奨されていません。本節以降はaddEventListenerでイベントハンドラを登録する方法でイベント処理を行います。
2-4-2 イベントオブジェクト
イベントハンドラとして登録する関数に引数を設定しておくと、イベント発生時に発生元オブジェクトを取得することができます。プログラム例を示します。
プログラム例(ch02/eventobject.html)
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>イベントオブジェクト</title> </head> <body> <button>あいさつ</button> <script src="eventobject.js"></script> </body> </html>
プログラム例(ch02/eventobject.js)
function clicked(e){ console.log(e.target.innerText); } const button = document.querySelector("button"); button.addEventListener("click",clicked);
実行結果
イベントハンドラとなる関数に引数e
を指定しています。この引数には、イベント発生時にイベントオブジェクトが渡されます。イベントオブジェクトはイベントに関する様々な情報を持つオブジェクトです。イベントオブジェクトの持つtarget
プロパティを使用するとイベント発生元の要素を取得できます。したがってe.target
はbuttonタグのオブジェクトです。innerText
プロパティを使って、タグで囲まれた文字列を取得・表示しています。
2-4-3 イベントハンドラの解除
登録したイベントハンドラは解除することも可能です。イベントハンドラの解除にはremoveEventListenerメソッドを使います。使い方はaddEventListenerメソッドとほぼ同様で、次のようになります。
removeEventListener(イベントの種類,解除したい関数)
プログラム例を示します。
プログラム例(ch02/removeevent.html)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>イベント解除</title> </head> <body> <button id="greeting">あいさつ</button> <button id="remove">解除</button> <script src="removeevent.js"></script> </body> </html>
プログラム例(ch02/removeevent.js)
function clicked(e){ console.log("Hello!"); } const button1 = document.querySelector("#greeting"); button1.addEventListener("click",clicked); function clicked2(e){ //clicked関数を解除する button1.removeEventListener("click",clicked); } const button2 = document.querySelector("#remove"); //「解除」ボタンを押すとclicked2が実行されるように設定 button2.addEventListener("click",clicked2);
実行結果
- 「あいさつ」ボタンにはclicked関数、「解除」ボタンにはclicked2関数をイベントハンドラとして登録しています。
- clicked2関数は、「あいさつ」ボタンの
click
イベントを解除しています。解除したいのはclicked関数なので、2つめの引数はclicked
としています。 - 「解除」ボタンを押した後は、「クリック」を押しても「Hello!」のメッセージが表示されなくなります。
2-5 様々なイベント処理
最後に様々なイベント処理を含んだプログラムを見てみましょう。
プログラム例(ch02/profile.html)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>プロフィール設定</title> <link href="profile.css" rel="stylesheet" /> </head> <body> <h2>プロフィール設定</h3> <a href="">ヘルプを見る</a> <h4>アバター画像</h4> <img src="avatar00.png"><br> <input type="radio" name="avatar" value="00" checked>女性1 <input type="radio" name="avatar" value="01">男性1 <input type="radio" name="avatar" value="02">男性2<br> <strong>ニックネーム</strong><br> <input type="text" name="nickname"><br> <br> <button>決定</button> <script src="profile.js"></script> </body> </html>
プログラム例(ch02/profile.css)
.onlink{ background-color: lightpink; } .error{ background-color: lightpink; }
プログラム例(ch02/profile.js)
function helpover(e){ e.target.className = 'onlink'; } function helpout(e){ e.target.className = ''; } function changeAvatar(e){ let imgname = 'avatar' + e.target.value + '.png' let img = document.querySelector('img'); img.src = imgname; } function validation(){ let elements = document.getElementsByName("nickname"); let nickname = elements[0]; if(nickname.value == ''){ nickname.className = 'error'; } else { nickname.className = ''; } } //リンク const link = document.querySelector('a'); link.addEventListener('mouseover',helpover); link.addEventListener('mouseout',helpout); //ラジオボタン const radios = document.querySelectorAll('input[name="avatar"]'); for(let radio of radios){ radio.addEventListener('change',changeAvatar); } //ボタン const button = document.querySelector('button'); button.addEventListener('click',validation);
プログラム中で用いられているアバター画像については、下記からダウンロードできます。
実行結果
ポイントを解説します。
mouseoverイベント
要素の上にマウスカーソルが乗った時に発生するイベントです。イベントハンドラとして登録されているhelpoverメソッドは次のようになっています。
e.target.className = 'onlink';
e.target
はカーソルが乗ったaタグのオブジェクトを表しています。className
プロパティはタグのclass属性に該当するもので、値を設定するとclass属性を追加できます。したがって、イベントが動作するとaタグが<a href="" id="help" class="onlink">
のようになります。
class属性がonlink
になっているものは、CSSで背景色をlightpink
に設定しているため色が変わります。
mouseoutイベント
要素の上に乗っていたマウスカーソルが離れた時に発生するイベントです。イベントハンドラとして登録されているhelpoutメソッドの処理は次のようになっています。
e.target.className = '';
aタグのclass属性に空文字を設定して、「無し」 にしています。結果的に背景色が元の色に戻ります。
changeイベント
要素が変化した際に発生するイベントです。ラジオボタンで使用しています。イベントハンドラとして登録した関数は次のようになっています。
let imgname = 'avatar' + e.target.value + '.png' let img = document.querySelector('img'); img.src = imgname;
e.target.value
はクリックされたラジオボタンのvalue属性を取得しています。3つのラジオボタンの各値は00
、01
、02
となっているので、そのまま画像ファイル名の一部として使っています。
次にimgタグのオブジェクトを取得し、src
プロパティを変更することでimgタグの画像変更を行っています。
clickイベント
要素をクリックした際に発生するイベントです。決定ボタンで使用しています。イベントハンドラとして登録しているvalidation関数の処理は次のようになっています。
let elements = document.getElementsByName('nickname'); let nickname = elements[0]; if(nickname.value == ''){ nickname.className = 'error'; } else { nickname.className = ''; }
validation関数の目的はニックネームのテキストボックスが空の場合に、テキストボックスに色をつけてエラーを知らせることです。
まず始めにgetElementsByNameメソッドで要素を取得しています。このメソッドは引数で指定した値とname属性の値が一致するタグを要素として取得します。 name属性の値は1つのWebページ内で重複しても問題ないため、該当するタグは複数になる場合があります。 そのため、戻り値は配列として返されます。
今回のHTML上ではname属性がnickname
となっているタグは下の1つしかないため、0番目の要素をテキストボックスと断定して取り扱うことにしています。
テキストボックス要素のvalueプロパティは、テキストボックスの入力内容を表します。nickname.value == ''
でテキストボックスが空かどうかをチェックし、空の場合はテキストボックスにclass属性を追加しています。
class属性がerror
の場合、cssファイルでは次のように定義しているため、テキストボックスが色つきで強調されることになります。
.error{ background-color: lightpink; }
querySelectorAllメソッド
ラジオボタンのイベントハンドラ設定は3つの要素に行わなければなりません。そのため要素の取得は下のようにquerySelectorAllメソッドを使用しています。
const radios = document.querySelectorAll('input[name="avatar"]'); for(let radio of radios){ radio.addEventListener('change',changeAvatar); }
querySelectorAllメソッドは、querySelectorメソッド同様に引数にセレクタを指定することで、該当する要素を返します。例ではセレクタ指定としてinput[name="avatar"]
と記述することで、name属性がavatar
になっているinput要素を指定しています。querySelectorメソッドとの違いは該当する要素のすべてを配列で返す点です。したがって、3つのラジオボタンが取得できることになります。そのためfor..of文を使用して、配列の各要素にイベントハンドラを登録しています。
おわりに
今回は以上となります。最後までご覧いただき、ありがとうございました。DOMの基礎的な部分ではありますが、理解の一助になれば幸いです。ひきつづき、よろしくお願いいたします。