JavaScriptによるプログラミングに関する世界観2(DOM/Canvas)です。
JavaScriptフレームワークも参照のこと。
自分の書いたブログ「神々とともに生きる詩人」2021/01/27より。
ブラウザはHTMLをCSSなどと同時に解釈し、HTMLデータを内部的なツリー構造であるDOMツリーに変換する。
DOMツリーはブラウザにおいては、JavaScriptと呼ばれる言語で操作することができる。
JavaScriptによる操作は、クライアントのWebブラウザの内部で行われるため、クライアントサイドと呼ばれる。
HTMLがブラウザに読み込まれると、HTMLの内容と同じツリー構造の「DOMツリー」が自動的に作られる。
JavaScriptでは、このDOMツリーを操作することで、HTMLを操作できる。
また、JavaScriptではWindowオブジェクトを使うことで、window.alert()関数から警告ボックスを出したり、window.documentプロパティからHTMLの内容にアクセスしたり、window.locationプロパティからURL情報を取得したりできる。
最初から何もしなくても用意されている関数やプロパティは、みんなWindowオブジェクト。window.は省略することができる。
以下のページを参考に執筆しました。
(以下はJavaScript初心者でもすぐわかる!DOMとは何か? - エンジニアの入り口を参考に執筆しました。)
DOMではツリー構造でノード(HTMLの要素)へアクセスすることができる。JavaScriptからはID名でノードを取得し、操作することができる。
HTMLの側には、
<div id="hoge"> <p>Hoge</p> <p>Foo</p> <p>Bar</p> </div>
とする。
まず、以下のようにすることで、このhoge要素を取得できる。
let hoge_element = document.getElementById('hoge');
ここで、hoge_element.styleプロパティを編集すれば、CSSが操作できる。
hoge_element.style.color = "blue";
また、hoge_element.childNodes[5]などを取得した上で、この子ノードのinnerHTMLを書き換えれば、子ノードが書き換えられる。
hoge_element.childNodes[5].innerHTML = "<p>Fuga</p>";
Webブラウザの仕様として、ノードとノードの間に空白のノードが挿入されるため、childNodes[5]は5番目の要素にはならず、3番目の要素となる。(後日注記:おそらく、タグとタグの間に含まれる空白の情報のことだと思われる。)
DOMを使うことで、簡単にHTMLの要素をオブジェクトとして変えることができる。HTMLを用意してある特定の要素を変える、という点ではPHPに似ているが、フォーマットに
<div id="hoge"></div>
などのID要素を指定した上で、JavaScriptやCSSなどから'hoge'とか'#hoge'などのIDで要素を特定しなければならない。(DOMを直接操作せずにフレームワークを用いることもできる。)
2023.04.21編集
JavaScriptで文字列をページに出力するためには、document.write()関数を使います。
document.write('<p>Welcome to my homepage.</p>');
これで、現在の位置(スクリプトを実行した位置)にHTMLを出力できます。
指定の位置に出力したい場合は、まず、HTMLにidをつけてdivタグを表示しておきます。
<div id="header"></div>
そして、DOMで以下のように操作します。
var header = document.getElementById("header"); header.innerHTML = "<p>Welcome to my homepage.</p>";
複数行にわたる文字列を入れたい場合は、行末に\をつけます。
応用として、
function OnMenuButtonClick() { ... }
のような関数の中でDOMを操作し、HTMLの側にリンクとして
<input type="button" value="Menu" onclick="OnMenuButtonClick();"/>
のようにすることが考えられます。
(ボタンのクリック時にJavaScriptを実行する (JavaScript プログラミング) - iPentecを参考に執筆・引用しました。)
document.write()は便利ですが、現在では非推奨となっています。
このため、insertAdjacentHTML()を代わりに使うことができます。
たとえば、
let div_page = document.getElementsByClassName('page'); div_page[0].insertAdjacentHTML('afterend', '<p>このホームページはAssyによって運営されています。</p>';
とします。afterendを指定すると要素の後ろに、beforebeginを指定すると要素の前に表示されます。
(以下はパーフェクトJavaScript (PERFECT SERIES 4)を参考に執筆しました。)
ノードの選択の方法は、
ノードの選択 | 説明 |
---|---|
Document.getElementById() | IDから検索する |
Element.getElementsByTagName() | タグ名から検索する(ワイルドカードとしての*も使用可) |
HTMLDocument.getElementsByName() | 名前(name属性)から検索する |
HTMLElement.getElementsByClassName() | クラス名から検索する |
などの方法がある。
JavaScriptは素直でないため、得られたエレメントが配列になっていることがある。このような時は添え字を加えて配列の要素としてアクセスする。
また、プロパティとして
プロパティ | 説明 |
---|---|
parentNode | 親ノード |
childNodes | 子ノードのリスト |
firstChild | 先頭の子ノード |
lastChild | 末尾の子ノード |
nextSibling | ひとつ後にある子ノード |
previousSibling | ひとつ前にある子ノード |
が取得できる。また空白や改行もテキストノードとして扱われる。
HTMLを記述する際には可読性からタグごとに改行を入れるが、この改行部分に空白ノードが存在するため、firstChildとした時にまず空白ノードが取得される。注意すること。
空白ノードが含まれるのが嫌なら、空白ノードを除去することのできる次のAPIがある。
プロパティ | 説明 |
---|---|
children | 子要素のリスト |
firstElementChild | 先頭の子要素 |
lastElementChild | 末尾の子要素 |
nextElementSibling | ひとつ後にある子要素 |
previousElementSibling | ひとつ前にある子要素 |
childElementCount | 子要素の個数 |
より柔軟なノードの指定にはXPathを使う。XPathを使うと、複雑な条件の指定(なんちゃらのなんちゃらの中のなんちゃらなど)を簡単に行える。
XPathよりシンプルな仕組みとしてはSelectors APIがあり、querySelectorAll()は指定した条件に一致する全ての要素を、querySelector()は指定した条件に一致する最初の要素を取得する。
また、ノードを作成・追加・変更するには、
ノードの作成 | 説明 |
---|---|
Document.createElement() Document.createTextNode() | ノードを作成する |
Document.createComment() | コメントを作成する |
注意点として、ノードを作成しただけではHTMLには何の変化もない。ノードをDOMツリーに追加してはじめてブラウザに表示される。
ノードの追加 | 説明 |
---|---|
Node.appendChild() | 最後の子要素に追加する |
Node.insertBefore() | ある位置に追加する |
Node.replaceChild() | 変更する。取得したノードのプロパティを書き換えてもよい。 |
Node.removeChild() | ノードの削除。 |
しかしながら、いちいちcreateElement()やappendChild()を使うのは面倒くさいので、HTMLElement.innerHTMLプロパティを使ってHTML文字列を取得・変更したり、textContentプロパティで子要素まで含めて文字列を取得・変更することもできる。
後日注記:基本的に、JavaScriptでDOMを操作したいなら、Document.createElement()で要素を作成し、HTMLElement.innerHTMLでそれを変更すればよい。以下のリンクが参考になる。
(以下はパーフェクトJavaScript (PERFECT SERIES 4)を参考に執筆しました。)
DOMとともに使われるJavaScriptの機能がイベントです。
イベントを使うことで、マウスのクリックやマウスポインタの要素の上への移動などを検出したり、感知してJavaScriptの関数を実行することができます。
JavaScriptでイベントを用いることで、イベントドリブンプログラムが可能です。JavaScriptのイベントドリブンでは、「イベントハンドラ」あるいは「イベントリスナ」を用います。
イベントハンドラは、たとえば上の例でいえば「onclick」です。これはマウスがクリックされた時に発生するイベントのイベントハンドラです(正確にはイベントハンドラがonclickで、イベントタイプがclick。マウスをクリックするとイベントclickがイベントハンドラ・イベントリスナに引数として与えられる)。これをHTMLに直接記述するか、DOM要素のプロパティに指定するか、あるいはdocument.getElementById()をした後にaddEventListener()を使ってイベントリスナを登録することで、イベントに関数を関連付けられます。
var hoge_button = document.getElementById('hoge'); hoge_button.addEventListener('click', function (e) { ... }, false);
イベントとDOMを組み合わせて使うことで、たとえばマウスがクリックされた時に特定のHTML要素を表示する、といったことが可能です。
たとえば、getElementById()で取得した特定の位置のHTML要素(エレメント)に、appendChild()で要素を追加したり、removeChild()で削除したり、innerHTMLでHTMLを挿入したり、といったことができます。
CSSを使って、display: none;を使って隠された要素のスタイルを変えることで、表示・非表示を切り替える、という方法もあります。
このような時に、CSSのセレクタを使うことで、position:absolute;で要素を絶対位置に表示したり、position:relative;である基準となる位置からの相対位置に表示したり、といったことができます。
上手く使えば、はてなブログのようにブログの編集画面を表示・非表示したり、メニュー項目を表示したり、画像を大きく表示したり、×ボタンを押した時に非表示にしたり、といったこともできます。
同じことはjQueryやVue.jsなどのライブラリやフレームワークを用いても可能です。
(おうちで学べるプログラミングのきほんより編集して引用。)
機能 | メソッド・プロパティ |
---|---|
HTML | document(表示されている文書、Documentオブジェクト) |
Webブラウザ | history(履歴、Historyオブジェクト) location(URI、Locationオブジェクト) name(ウィンドウ名) navigator(Webブラウザ情報、Navigatorオブジェクト) |
ダイアログ | alert()(警告ダイアログ) confirm()(確認ダイアログ) print()(印刷ダイアログ) |
周期・時限実行 | setInterval()(指定時間ごとに繰り返し周期実行する関数を設定) setTimeout()(指定時間後に1度だけ時限実行する関数を設定) clearInterval()(setInterval()の設定をクリア) clearTimeout()(setTimeout()の設定をクリア) |
canvas要素を使うことで、JavaScriptから図を描くことができる。
以下にサンプルコードがある。
たとえば、HTMLに
<canvas id="paint_area"></canvas>
とした上で、JavaScriptから、
var paint_area = document.getElementById("paint_area"); var context = paint_area.getContext("2d");
とする。このcontextに対して、スタイルを設定(fillStyleやlineWidthなど)したり、さまざまな図形描画用のAPI(beginPath(), closePath(), moveTo(), lineTo(), fill(), stroke()など)を使って図を描画することができる。
Ajaxを参照のこと。
jQueryを参照のこと。