ウェブページやアプリを書く場合に、最も多く必要になるのはウェブ文書をどうかして操作する事でしょう。これは普通ドキュメントオブジェクトモデル(Document Object Model、DOM)、これはHTMLとスタイルに関する情報をDocumentオブジェクトを使いまくって操作する一連のAPIです。この記事では、DOMの使い方を詳しく見ながら、面白い方法であなたの環境を変える事ができる興味深い他のAPIもいくつか見ていきます。

前提条件: 基本的なコンピュータに関する知識と理解、HTMLとCSS、JavaScript—JavaScriptのオブジェクトについても—基本を理解していること
目的: DOM APIの核と、DOMと共によく利用されるAPI、ドキュメントの操作について詳しくなること

ウェブブラウザーの重要なパーツ

ウェブブラウザーはとてもたくさんの動いている部品からなるソフトウェアの複雑な集合体で、部品の多くはウェブ開発者のJavaScriptからでは制御したり操作することはできません。こんな制約はよろしくないと思う方もいるかもしれませんが、ブラウザが保護されているのには十分な理由があって、これは主にセキュリティ関係のためです。もしあるウェブサイトがあなたが保存しているパスワードやその他の秘密情報にアクセスできて、あなたのふりをして他のサイトにログインできたらどうですか?

制限はあっても、ウェブAPIは、ウェブページ上でいろいろ素敵な事をできるように、たくさんの機能を提供してくれます。あなたのコードからよく参照するであろう目に見える代物はほんのわずかです — 下の図を見て下さい、この図はウェブページの表示に直接関与しているブラウザーの主要なパーツを表わしています:

  • ウィンドウはウェブページが読み込まれる部分の回りのブラウザーの枠です。これはJavaScriptではWindowオブジェクトで表わされます。このオブジェクトに備わるメソッドを使って、ウィンドウの大きさを調べたり(Window.innerWidthWindow.innerHeightを参照)、ウィンドウに読み込まれる文書を操作したり、その文書に関係するデータをクライアント側(例えばローカルデータベースや他のデータ保存機構)で保存したり、現在のウィンドウに対してイベントハンドラー を追加したり、などできます。
  • ナビゲータはブラウザーの状態やウェブで使われているようなブラウザーの身元(つまりユーザーエージェント)を表わします。JavaScriptではNavigatorオブジェクトで表わされます。このオブジェクトを使って、位置情報、ユーザが好む言語、ユーザのウェブカムからの録画データ、などを取得できます。
  • ドキュメント(ブラウザーではDOMとして表現されます)はウィンドウに実際に読み込まれているページのことで、JavaScriptではDocumentオブジェクトで表わされます。このオブジェクトを使って文書を構成するHTMLとCSS上の情報を調べたり操作したりできて、例えばDOMの中のある要素に対する参照を得たり、その中身のテキストを変更したり、新しいスタイルを適用したり、新しい要素を作成して現在の要素の子に追加したり、一緒くたに削除したりできます。

この記事では主にドキュメントの操作に着目しますが、それ以外の役に立つこともちょっとお見せしていきます。

ドキュメントオブジェクトモデル

あなたのブラウザーの一つ一つのタブに今読み込まれているドキュメントは、ドキュメントオブジェクトモデルとして表現されます。これはHTMLの構造に対してプログラム言語から簡単にアクセスできるようにブラウザーが作成する、"木構造"による表現です — 例えば、ページをレンダリングする際にはブラウザー自体がスタイルや他の情報を適切な要素に適用するためにDOMを使い、ページのレンダリングが終わった後にはあなたのような開発者がJavaScriptを使ってDOMを操作できます。

dom-example.html にちょっとした例を作成しました(ライブ実行もどうぞ)。ブラウザーから開いてみてください — これはとても簡素なページで、<section>要素の中に画像が一つと、一つのリンクを含む一つのパラグラフがあります。HTMLのソースはこんな感じです:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Simple DOM example</title>
  </head>
  <body>
      <section>
        <img src="dinosaur.png" alt="A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human, with small arms, and a large head with lots of sharp teeth.">
        <p>Here we will add a link to the <a href="https://www.mozilla.org/">Mozilla homepage</a></p>
      </section>
  </body>
</html>

一方これのDOMはこんな具合になります:

注記: このDOMツリーの図は Ian Hickson の Live DOM viewer を使って作成しました。

これを見ると、それぞれのドキュメント内の要素とちょっとばかりのテキストそれぞれが、ツリーの中でそれ自身のエントリーがあるのがわかるでしょう — これら一つ一つをノードと呼びます。またノードの種類を示す語や、ノードそれぞれの関係によりツリーでの位置があるのがわかるでしょう:

  • エレメント(要素)ノード: DOMの中でのHTML要素です。
  • ルート(根)ノード: 木の頂点のノードで、HTMLの場合であれば常にHTMLノードになります。(SVGや独自のXMLといった他のマークアップ言語の方言では異なるルート要素の場合があります)
  • 子ノード: 他のノードに直結して含まれるノードです。上の例だと、例えばIMGSECTIONの子ノードとなります。
  • 子孫ノード: 他のノードにどのような形であれ含まれるノードです。上の例だと、例えばIMGSECTIONの子ノードであり、子孫ノードでもあります。IMGBODYの二段階内側にあるのでBODYの子ノードではありませんが、BODYの子孫ノードではあります。
  • 親ノード: その中に他のノードを持つノードです。例えば上の例だとBODYSECTIONノードの親ノードになります。
  • 兄弟ノード: DOMツリーの同じ階層にあるノードです。上の例だとIMGPは兄弟ノードになります。
  • テキストノード: テキスト文字列を含むノードです。

これからコードを見ていくとこういう語が頻出するので、DOMを使い始める前に、これらの用語をしっかり覚えておくと良いでしょう。CSSの勉強をしているときも、これらの語をみかけることでしょう(子孫セレクター、子セレクターとか)。

実践学習: 基本的なDOM操作

DOM操作の学習スタートは、実践的な例から始めましょう。

  1. dom-example.htmlimage のローカルコピーを一緒に作成して下さい。
  2. <script></script> 要素を、閉じ</body>タグのすぐ上に追加して下さい。
  3. DOMの中の要素を操作するため、まずDOMを選びだしてこれへの参照を変数に保存する必要があります。script要素の中に、次の行を追加して下さい:
    var link = document.querySelector('a');
  4. 要素への参照を変数に保存したので、これが備えているプロパティとメソッドを使ってDOMの操作を始められます(利用できるプロパティとメソッドは、たとえば<a>要素であればHTMLAnchorElementインターフェース、さらにその汎化した親のインターフェースHTMLElementNode — これはDOMの全てノードが相当します — で定義されています)。まずは、リンクの中のテキストを、Node.textContentプロパティを更新する事で変更してみましょう。上で書いた行の下に、次の行を追加して下さい:
    link.textContent = 'Mozilla Developer Network';
  5. クリックされたときに変な場所に行かないよう、リンクが指す先のURLも変えておくべきでしょう。また下に、以下の行を追加して下さい:
    link.href = 'https://developer.mozilla.org';

JavaScriptあるあるですが、要素を選んで変数に保存する方法にはいろんなやり方があることを頭に入れておいて下さい。Document.querySelector()を使うのが推奨される今風のやり方ですが、これはCSSセレクタと同じ方法で要素を選別できるからです。上記のquerySelector()呼び出しでは文書に現われる最初の<a>がマッチします。もし複数の要素を選択し処理したいのであればDocument.querySelectorAll()を使うことができて、これはセレクタとマッチする全ての要素にマッチし、それらへの参照をNodeListと呼ばれる配列のようなオブジェクトに保存します。

要素への参照を得るための、次のような古いやり方もあります:

  • Document.getElementById()は要素を指定のid属性値を使って選択します。<p id="myId">My paragraph</p>こんなのです。 関数の引数にIDを渡します。 var elementRef = document.getElementById('myId')こんな具合です。
  • Document.getElementsByTagName()これは指定した種類の全ての要素を配列として返します、例えば全部の<p>、全部の <a>など。 要素の種別は関数の引数として渡します。var elementRefArray = document.getElementsByTagName('p')こんな具合です。

上の二つは querySelector()のような今風のメソッドよりも古いブラウザーで動作しますが、あまり便利ではありません。これ以外にどんなやり方があるかは、あなた自身で探してみて下さい!

新しいノードの作成と配置

ここまでで、どんな事ができるのかちょっと見えてきたと思いますが、さらに進んで新しい要素を作る方法を見ていきましょう。

  1. 今の例題に戻って、<section>要素を掴むところから始めましょう — すでに書いてあるスクリプトの下に次のコードを追加して下さい(この先の他の行についても、同じようにやって下さい):
    var sect = document.querySelector('section');
  2. Document.createElement()を使って新しいパラグラフを作り、前やったのと同じ方法でテキストを入れてやりましょう:
    var para = document.createElement('p');
    para.textContent = 'We hope you enjoyed the ride.';
  3. この新しいパラグラフをsectionの最後にNode.appendChild()を使って追加できます:
    sect.appendChild(para);
  4. このパートの締めとして、文章がうまいことまとまるように、リンクを含んでいるパラグラフに対してテキストノードを追加しましょう。まずテキストノードをDocument.createTextNode()を使って作成します:
    var text = document.createTextNode(' — the premier source for web development knowledge.');
  5. リンクを含んだパラグラフへの参照を取り出して、そこにテキストノードを追加します:
    var linkPara = document.querySelector('p');
    linkPara.appendChild(text);

以上がDOMにノードを追加するために必要な事のほぼ全てです — 動的なインターフェースを作成する際(あとでそういう例題をいくつか見ていきます)これらのメソッドをめっちゃ使う事になるでしょう。

要素を移動したり削除したり

ノードを移動したり、DOMから削除したくなる場合があると思います。勿論できます。

リンクを含むパラグラフを section の最後に移動したい場合は、こうするだけです:

sect.appendChild(linkPara);

これでパラグラフはsectionの一番下に移動します。コピーが作成されるだけじゃないのかとお思いかもしれませんが、この場合は違います — linkPara はパラグラフへの参照の唯一のコピーです。もしコピーをした上で同じように追加をしたいのであれば、Node.cloneNode()をかわりに使う必要があります。

削除したいノードとその親ノードへの参照を得ていれば、ノードを削除するのも非常に簡単です。今の例題であれば、以下のようにNode.removeChild()を使うだけです:

sect.removeChild(linkPara);

よくあるケースですが、削除したいノードそのものへの参照しかない場合に、そのノードを削除するのはちょっとばかり複雑になります。ノード自体に消えろと命じるメソッドはないので、次のようにしなければなりません。

linkPara.parentNode.removeChild(linkPara);

上の行をあなたのコードに追加してやってみて下さい。

スタイルを操作する

いろんなやり方でCSSスタイルをJavaScriptから操作することができます。

まず、ドキュメントに付随する全部のスタイルシートのリストはDocument.stylesheetsを使って得られ、これはCSSStyleSheetオブジェクトの配列を返します。そうしたらお望みのままにスタイルを追加追加したり削除したりできます。ですがこのやり方について詳しくはやりません。なぜならスタイルをいじるにはちっとばかり古風で難しいやり方だからです。もっと簡単なやり方があります。

まずは、動的にスタイルを指定したい要素に、インラインスタイルを直接追加するやり方です。これにはHTMLElement.styleプロパティを使い、このプロパティはドキュメント中の各素要のインラインスタイル情報を保持しています。このオブジェクトのプロパティを更新すれば要素のスタイルを直接変更できます。

  1. 例として、作成中の例題に以下の行を追加してみて下さい:
    para.style.color = 'white';
    para.style.backgroundColor = 'black';
    para.style.padding = '10px';
    para.style.width = '250px';
    para.style.textAlign = 'center';
  2. ページをリロードすると指定のパラグラフにスタイルが適用されているはずです。ブラウザーのPage InspectorやDOM inspector,からパラグラフを見ると、言うまでもなく上の行がドキュメントのインラインスタイルに追加されているはずです:
    <p style="color: white; background-color: black; padding: 10px; width: 250px; text-align: center;">We hope you enjoyed the ride.</p>

注記: CSSではハイフン記法になっているものを、JavaScriptプロパティ版のCSSスタイルはどんな風に小文字のキャメルケースで書いている(background-colorbackgroundColorとか)か見ておいて下さい。まぜこぜにしないよう注意して下さい、さもないと動きませんよ。

ドキュメントのスタイルを動的にいじる際によく使われる別のやり方をこれから見ていきましょう。

  1. さっきJavaScriptに追加した5行を削除します。
  2. HTMLの<head>の中に、以下を追加します:
    <style>
    .highlight {
      color: white;
      background-color: black;
      padding: 10px;
      width: 250px;
      text-align: center;
    }
    </style>
  3. さて、多くのHTML操作においてとても役に立つメソッドをお見せします — Element.setAttribute() — これはに二つの引数、要素に設定したい属性名と、属性に設定したい値、を与えます。この場合だと、我々のパラグラフにクラス名、highlightをセットします:
    para.setAttribute('class', 'highlight');
  4. ページをリロードしても何も変わりません — パラグラフにはCSSが今も適用されていますが、今回はクラスを指定してCSSルールが選んでいて、インラインCSSスタイルによるものではありません。

どうやるかはあなた次第です。それぞれに利点と欠点があります。最初のやり方は少ない設定ですみ、簡単な場合には向いていますが、二つ目のやり方はずっときれいです(よくないやり方とされる、CSSとJavaScriptの混在やインラインスタイルの使用がありません)。もっと大規模で複雑なアプリを作り始めたら、多分二つ目のやり方をよく使うようになると思いますが、結局はホントにあなた次第です。

ここまで、実はそれほど役に立つことをやってません! 静的なコンテンツの作成にJavaScriptを使う利点はありません — JavaSCript など使わず、普通にHTMLに書けば良いんです。HTMLよりややこしいですし、コンテンツをJavaScriptで作成するのは他にも問題があります(検索エンジンで読めない、とか)。

次の二つのセクションでは、DOM APIのもっと実践的な使い方を見ていきます。

注記: 私たちによる dom-example.html完成版 のデモがGitHubにあります (ライブ実行もどうぞー)。

実践学習: ウィンドウオブジェクトから使える情報を取り出す

ここまででは文書を操作するための NodeDocument の機能ばかり見てきましたが、他のソースからデータを取ってきてあなたの UI で使ったって勿論かまわないわけです。前の記事でお見せしたシンプルな maps-example.html のデモを思いだして下さい — これでは位置に関する情報を取得してあなたの周りの地図を表示するに利用しました。 あなたはデータが正しい形式である事を確認するだけです。これは JavaScript が弱い型付け言語であるために、他の多く言語の場合よりも簡単です — 例えば画面に表示しようとしたとき、数値は自動的に文字列に変換されます。

ここの例題ではよくある問題を解決していきます — あなたのアプリを表示しているウィンドウがどんな大きさであれ、それ同じ大きさになるようにすることです。これはゲームのような、表示する画面領域をできるだけ大きくしたいような場合に、しばしば役に立ちます。

まずは window-resize-example.htmlbgtile.png ファイルのローカルコピーを作成して下さい。読み込んで見てみて下さい — 背景に画像がタイル表示された、<div> 要素が画面に小さく表示されているでしょう。この領域が、私たちのアプリのUI領域だとしていきます。

  1. まず最初に、div への参照を取得し、ビューポート(ドキュメントが表示されている内側のウィンドウです)の幅と高さを取得して、これらを変数に保存します。便利なことに幅と高さの値は Window.innerWidthWindow.innerHeight プロパティにあります。以下の行を、もう書いてある <script> の中に書き足します:
    var div = document.querySelector('div');
    var WIDTH = window.innerWidth;
    var HEIGHT = window.innerHeight;
  2. 次は、動的に div の幅と高さをビューポートのものと同じにします。次の二行を、さっき追加した部分の後に書き足して下さい:
    div.style.width = WIDTH + 'px';
    div.style.height = HEIGHT + 'px';
  3. 保存してブラウザーで読み直してみて下さい — どんな大きさの画面を使っているのであれ、div がビューポートと同じ大きさになったはずです。ウィンドウが大きくなるようにリサイズしてみても、div の大きさは変わらないはずです — 一度しか大きさを設定していないからです。
  4. ウィンドウがリサイズされた時に div もリサイズされるよう、イベントを使ってみるのはどうでしょう? Window オブジェクトにはリサイズされた時に呼ばれるイベントがあって、ウィンドウがリサイズされる毎発火します — この機能を Window.onresize イベントハンドラーから使って、リサイズされる毎私たちのコードが再実行されるようにしてみましょう。あなたのコードの最後に以下を書き足して下さい:
    window.onresize = function() {
      WIDTH = window.innerWidth;
      HEIGHT = window.innerHeight;
      div.style.width = WIDTH + 'px';
      div.style.height = HEIGHT + 'px';
    }

注記: もし行き詰まったら、私たちによる 完成版ウィンドウリサイズ例題 (ライブ実行版もあるよ) を見て下さい。

実践学習: 動的な買い物リスト

この記事の締めとして、あなたにちょっとした難題を出したいと思います — 単純な買い物リストの例を作ってもらいます。フォーム入力(input)とボタンからリストに動的に商品を追加できるようにします。input に商品を入力してボタンを押したら:

  • 商品がリストに表示されなければならない。
  • それぞれの商品にはボタンが付いていて、それを押すとその商品をリストから消せなければならない。
  • 次の商品をすぐに入力できるよう、input の中身は消されてフォーカスされていなければならない。

完成版のデモはこんな感じになるでしょう:

この課題を完了させるには、以下のステップに従い、上で説明した通りに買い物リストが動くようにして下さい。

  1. まず私たちが用意した shopping-list.html 初期ファイルをダウンロードしてローカルコピーをどこかに作成します。最小限の CSS、ラベルのついたリスト、inputとボタン、空のリストと <script> 要素が書いてあるはずです。この先書き足していくものは全部 script の中に書きます。
  2. (<ul>) と <input><button> 要素への参照を保持する3つの変数を作成します。
  3. ボタンがクリックされた時の応答として走らせる 関数 を作成します。
  4. 関数本体は、input 要素の現在の を変数に保存するところから始めます。
  5. 次に、input 要素の値に空文字列('')を代入して、input 要素を空にします。
  6. 3つの要素をを作成します — リスト項目(<li>) と <span><button> で、これらを変数に保存します。
  7. span と button をリスト項目 li の子に追加します。
  8. spanのテキストコンテントに、先程保存した input 要素の値を代入し、ボタンのテキストコンテントを「削除」にします。
  9. できたリスト項目をリストの子に追加します。
  10. 削除ボタンにイベントハンドラーを追加して、クリックされたらボタンが含まれているリスト項目全体を削除するようにします。
  11. 最後に、focus()メソッドを使って input 要素にフォーカスし、次の買い物リスト商品をすぐに入力できるようにします。

注記: 本当にどうしようもなく詰まったら、私たちの 完成版買い物リスト (ライブ実行版もあるよ)を見て下さい。

まとめ

私たちのドキュメントとDOM操作に関する学習はこれで終わりです。ここまでくれば、ドキュメントの制御やユーザのウェブ体験に関するブラウザーの重要な部品は何か、理解できたと思います。一番大事なDOMとは何か、役に立つ機能を作るのにこれをどう使えば良いのか理解できたと思います。

参考文献

ドキュメントをいじるのに役立つ機能はたくさんあります。私たちのリファレンスも見て、いろいろ発見して下さい:TDocument

(私共の Web API index から、MDNにあるウェブAPIに関する全ドキュメント一覧も見て下さい!)

ドキュメントのタグと貢献者

このページの貢献者: silverskyvicto, i12o
最終更新者: silverskyvicto,