Document Object Model

HTML文書をプログラミング言語でアクセスするためのモデルが Document Object Modelである。

文書ツリーとDOM

以下のHTML文書片を考える。

<body>
<p>今回は<em>三権分立</em>がテーマです。</p>
<ul>
 <li>司法</li>
 <li>立法</li>
 <li>行政</li>
</ul>
<p>の3つが…</p>
</body>

この文書には、以下のような要素の木構造ができている。

               +--「今回は」
               |
        +-[p]--+-[em]--「三権分立」
        |      |
        |      +--「がテーマです。」
[body]--+
        |      +-[li]--「司法」
        |      |
        +-[ul]-+-[li]--「立法」
        |      |
        |      +-[li]--「行政」
        |
        |
        +-[p]--「の3つが…」

図: 要素の木

これと全く同じ木構造が、HTMLユーザエージェントに付随するスクリ プト言語のオブジェクト(複合的な値)として生成される。これを規定したものが DOM (Document Object Model)である。

JavaScriptでのDOM

JavaScriptでは、javascriptプログラムを読み込んだときのHTML文書の あらゆる要素が、JavaScript言語の HTMLElement(の派生) オブジェクトとして 生成されている。「図:要素の木」が示す構造の場合、最初のbody要素を 意味するオブジェクトにアクセスできれば、その子孫の全ての要素オブジェクト にアクセスできる。また、HTML要素に付けられた属性や、スタイルシートの 特性値も自在に取得/設定できる。

documentオブジェクト

documentオブジェクトは、HTML文書全体の情報を保持する。 スタイルシートの操作で必要になるのは、 HTML文書中の特定の要素を探し出す方法で、 そのための関数には以下のものがある。

document.getElementsByTagName(ELEM)

HTML文書中から、ELEM で指定した要素を全て探し、 それらを配列にしたものを返す。

document.getElementById(ID)

HTML文書中から、ID と同一の id 属性を持つ 要素を返す。id属性は文書中で唯一の値を持たせることが前提であるので 同じid属性値が見付かった場合の挙動は定義されていない。

たとえば、上記のHTML断片例で「全てp要素のうち2つ目にあるもの」は、 以下のように JavaScript 変数 p2 に得られる。

var p, p2;					// 変数の使用を宣言する
var p = document.getElementsByTagName("p");	// 全ての p 要素を得る
if (p.length) {
  p2 = p[1];					// 添字1要素が2番目
}

また、CSSでのセレクタ指定を用いて一覧を得る関数もある。

document.querySelector(CSS-SELECTOR)

HTML文書中から、CSS-SELECTOR で指定した要素を全て探し、 それらのうち文書中最初に登場するものを返す。セレクタはたとえば "p.shout" のようなCSSセレクタ規則を指定する。

document.querySelectorAll(CSS-SELECTOR)

上記と同じだが、CSS-SELECTOR で指定した要素全ての リストを返す。

querySelectorによって得られた要素オブジェクトは、 StaticNodeListオブジェクトといい、その値に変更を加えてもDOMツリーには 影響が出ない。

HTML要素の属性へのアクセス

特定の要素を持つオブジェクトから、その要素に設定された属性には、

を介してアクセスできる。例えば、以下のようなHTML要素記述があったとする。

<p>鳥海山:<img src="chokai.jpg" alt="Mt.Chokai" 
width="400" height="266"></p>

このうち、img要素に設定された width, height 属性を半分にするスクリプトを作成してみる。JavaScriptプログラムから このimg要素を探しやすいように、id属性を追加し、画像クリックによって 関数が起動するように onclick 属性を追加し、以下のように書き換えておく。

<p>鳥海山:<img src="chokai.jpg" alt="Mt.Chokai" 
id="photo01" onclick="hanbun();" width="400" height="266"></p>

getAttribute/setAttributeを利用

JavaScriptプログラムファイル chokai1.js を開き、hanbun()関数を以下のように作成する。

function hanbun() {
  chokai = document.getElementById("photo01");
  w = chokai.getAttribute("width");
  h = chokai.getAttribute("height");
  chokai.setAttribute("width", w/2);
  chokai.setAttribute("height", h/2);
}

このスクリプトをロードするようHTML文書に以下のscript要素を追加する。

<p>鳥海山:<img src="chokai.jpg" alt="Mt.Chokai" 
id="photo01" onclick="hanbun();" width="400" height="266"></p>
<script type="text/javascript" src="chokai1.js">
</script>

作成例(文書のソースを見よ)

属性名と同じ変数を利用

JavaScriptプログラムファイル chokai2.js を開き、hanbun()関数を以下のように作成する。

function hanbun() {
  chokai = document.getElementById("photo01");
  w = chokai.width;
  h = chokai.height;
  chokai.width = w/2;
  chokai.height = h/2;
}

作成例(文書のソースを見よ)

属性値はHTML文書で明示的に定義されていない場合、取得できない場合があ る。スクリプトで操作したい場合はHTML文書中であらかじめ値を与えておく よう注意する。

CSSのプロパティへのアクセス

要素オブジェクトに属するstyleオブジェクトを介してスタイルシートの プロパティがアクセスできる。特性名にハイフンが含まれるものは ハイフンを取って、次の文字を大文字にして結合する。たとえば、

<div id="foo" style="background-color: yellow">
...
</div>

というdiv要素の場合は、以下のJavaScriptで background-color 特性値が 取得できる。

x = document.getElementById("foo");
x.style.backgroundColor;

この変数に代入することで、特性値を変更することもできる。

以下の例は、マウスが入ると赤に、出ると黄色に戻る枠を作成するものであ る。

redyellow.html

<html>
<head><title>Red and Yellow</title>
<style type="text/css">
<!--
div#box {
  width: 200px; height: 100px; background-color: yellow;
}
-->
</style>
</head>

<body>
<p>マウスを入れると赤、出すと黄色。</p>
<div id="box" onmouseover="red();" onmouseout="yellow();">
</div>
<script type="text/javascript" src="redyellow.js">
</script>
</body>
</html>

redyellow.js

function red() {
  document.getElementById("box").style.backgroundColor = "red";
}
function yellow() {
  document.getElementById("box").style.backgroundColor = "yellow";
}

定義例: redyellow.html

CSSの特性値の場合も属性のときと同様、HTML+CSS文書であらかじめ 値を設定していない特性値はスクリプトからは取得できない(nullが返る)。

文書とスタイルの分離という観点からは、CSSの特性値を操作する この方法が最も望ましい。

ノード間遷移

ひとつの要素オブジェクトが見付かると その他の要素も見付けることができる。「図:要素の木」が示すように、 あらゆる要素は親、兄弟、子を持つ可能性がある。このような性質を持つものを 一般的にノードという。特定の要素をノードとみなし、木の 親兄弟を辿るいくつかの値が定義されている。

プロパティ意味
parentNode親ノード
childNodes全ての子ノードを収めたリスト
firstChild最初の子ノード
lastChild最後の子ノード
previousSiblingひとつ前の兄弟ノード
nextSiblingひとつ次の兄弟ノード

また、見つかったノードそのものの情報にアクセスするための以下の値が利用できる。

プロパティ意味
tagName要素の名前
id要素がid属性を持っている場合その値
innerHTML要素の内部のHTML文そのもの (代入すると文書を更新できる)
style要素に施されているスタイルシートの集合

以下の例で考える。

<body>
<p id="top">今回は<em>三権分立</em>がテーマです。</p>
<ul>
 <li>司法</li>
 <li>立法</li>
 <li>行政</li>
</ul>
<p>の3つが…</p>
</body>

1つ目のp要素は、id="top" とIDが振ってあるのでこれを足掛かりに、

となる。これらを辿るプログラム例を示す(dom-demo.html)。

dom-demo.js

function domDemo() {
    // 全てのp要素一覧を p に得る
    var p = document.getElementsByTagName("p");
    // 1つ目のp要素を p1 に得る
    var p1 = p[0];			// 必ずp要素が見つかる場合に限る
    // p1の親を body に得る
    var body = p1.parentNode;
    // p1の子要素を全て得る
    var pChildren = p1.childNodes;

    // bodyの背景色を黄色に設定する。
    body.style.background = "yellow";
    // p1 の子すべての文字色を赤にする
    for (var i=0; i<pChildren.length; i++) {
	if (pChildren[i].style)
	    pChildren[i].style.color = "red";
    }
    // body要素の子要素全てのうちulがあればborderを紺の二重枠にする
    var allChildren = body.childNodes, c;	// 変数cもここで宣言
    for (i=0; i<allChildren.length; i++) {
	c = allChildren[i];
	// console.log(c.tagName);
	if (c.tagName && c.tagName.match(/ul/i)) {
	    c.style.border = "navy 3px double";
	}
    }
}
window.onload = domDemo;

参考文献

yuuji@koeki-u.ac.jp