ドキュメントオブジェクトモデル (DOM) は、XUL 要素に対して、要素の情報を取得したり、変更を行うために使用できます。
DOM の概要
ドキュメントオブジェクトモデル (DOM) は、XUL ノードをツリー構造で保持するために用いられます。 XUL ファイルが読み込まれるとき、タグが解析されて、1 つのタグ、または 1 つのテキストブロックを 1 つのノードとする階層型の文書構造 (DOM 構造) に変換されます。 また、さまざまなメソッドが DOM 構造の内容を調べたり、変更したりするために提供されており、これらを利用して目的の操作を行うことが可能です。 さらに、一部の XUL 要素には、追加の関数が提供されており、これらを使うことも可能です。
個々の XUL ファイルが読み込まれたとき、対応する文書 (document) オブジェクトが、ウィンドウやフレーム内に表示するために作成されます。 ウィンドウには、同時に 1 つの文書しか関連付けることができませんが、色々なメソッドを使って付加的な文書を読み込ませることは可能です。
Mozilla では、JavaScript を使って DOM にアクセスしたり操作したりすることが可能です。 多くの DOM オブジェクトが、スクリプトからアクセス可能な関数を持っていますが、 DOM が「JavaScript からアクセス可能な API のひとつである」ことは重要なので覚えておいてください。 別の言い方をすれば、JavaScript 自身は、純粋なスクリプト言語であり、スクリプトからこれらのオブジェクトにアクセスできるのは、Mozilla がこれらのオブジェクトへのアクセス方法を提供しているからにすぎないということです。
JavaScript では、グローバルオブジェクトが常に 1 つ存在して、いつでも利用することが可能になっています。
このグローバルオブジェクトは、(object.property のような形式で) オブジェクト名で修飾しなくともプロパティやメソッドの参照ができます。
例えば、このグローバルオブジェクトが 「name
」というプロパティを持っている場合、
name
がどのオブジェクトに属しているかを指定しなくても、単に「name = 7
」 というコードだけで変更することが可能です。
ブラウザコンテキストでは、ウィンドウがグローバルオブジェクトになっており、これは XUL の場合であっても該当します。
したがって、このグローバルオブジェクトはウィンドウごとに異なることになります。
また、フレーム構成の場合、各フレームはそれぞれ別個のウィンドウオブジェクトを持っています。
また、ウィンドウは、しばしば window
プロパティを使って参照されます。
この指定はオプションなのですが、参照しているメソッドのスコープを明示するために、ときどき利用されます。
例えば、以下の 2 つの行は、どちらも新しいウィンドウを開くために、同一の機能を呼び出します。
window.open("test.xul","_new"); open("test.xul","_new");
スクリプトのトップレベルで、つまり他の関数の外部で関数や変数を宣言すると、実際にはグローバルオブジェクトのプロパティを宣言していることになります。 XUL でも、宣言した関数は、それぞれウィンドウオブジェクトのプロパティとして設定されます。 例えば、以下のコードは、「Message」というテキストで 2 回アラートを表示します。
function getText(){ return "Message"; } alert(getText()); alert(window.getText());
このため、他のウィンドウで使用されているスクリプトの中で宣言されている変数にアクセスしたり、関数を呼び出したりするために必要なことは、そのウィンドウの window
オブジェクトを使用することだけです。
例えば、上の 2 つの例を 1 つのスクリプトファイルにまとめて、getText() 関数を、他のウィンドウ (開いた test.xul ウィンドウ) の中から呼び出したいとします。
これは、以下のようにして行うことが可能です。
alert(window.opener.getText());
各ウィンドウには opener
プロパティがあり、そのウィンドウを開いたウィンドウオブジェクトを保持しています。
この例では opener
で、開き元のウィンドウを取得して、そこで使われているスクリプトの中で宣言された getText()
関数を呼び出しています。
なお、opener
プロパティを window
という識別子で修飾しているのは、opener
が、window
のプロパティであることを明示するためであることを補足しておきます。
window
の open()
メソッドは、新しいウィンドウへの参照を返すので、開き元のウィンドウからも、新しいウィンドウの関数を呼び出すことが可能です。
しかしながら、これを利用するときに考慮するべき重要な注意事項が 1 つあります。
それは、open()
メソッドは、ウィンドウが完全にロードされる前に戻るために、戻った直後は、新しいウィンドウ上の関数が、まだ利用可能な状態になっていないことが多いという点です。
ウィンドウオブジェクトは、DOM のどの水準の仕様にも定義されていませんが、Mozilla では、しばしば、これを「DOM 水準 0」に含まれているとみなします。
「DOM 水準 0」とは、一部の開発者の間で、(NN 3.0 か IE3.0 に含まれる関数で) DOM と似たコンセプトの関数ではあるものの、仕様化が及んでいないものを示すために使われていた名前です。
ウィンドウに表示されている実際の文書は、window
の document
プロパティを使って取得できます。
なお、document
プロパティは、window
の中でも最もよく参照されるプロパティの 1 つであるため、 通常は「window.
」で修飾せずに参照します。
Mozilla は、文書の種類に応じて、いくつか異なる文書オブジェクトを提供しています。 主要な文書としては、HTML、XML、XUL の 3 つがあり、HTMLDocument、XMLDocument、XULDocument が、それぞれ対応する文書オブジェクトになります。 言うまでもないですが、最後の文書型が XUL 用です。 これら 3 つの文書型は、類似している点が多いため、 実際のコードについても、同一の基本実装を共有しています。 また逆に、いずれかの文書型に固有な関数もいくつか存在しています。
要素の取得
文書中の要素を取得するためには、要素に id
属性を設定しておき、 document
の getElementById()
メソッドを使うのが、最もよく利用される方法になります。
このため、このチュートリアルでも、ファイル検索ダイアログの多くの要素に、
属性を設定しています。
例えば、以下のコードによって、チェックボックスの状態を知ることができます。
id
var state = document.getElementById('casecheck').checked;
この、casecheck
という値は、「case sensitive」 チェックボックスの
と対応しています。
チェック状態の取得が済んだら、その結果を検索の実行をするときの指示として使用できます。
それ以外のチェックボックス、あるいは id
属性が設定された任意の要素についても、おおむね同じように処理することが可能です。
例えば、テキスト入力欄から、入力されたテキストの取得が必要といった場合にも、利用できます。
id
ファイル検索ダイアログの例
ファイル検索ダイアログが表示されたときに、最初からプログレスバーや、検索結果のためのダミーのツリーデータが表示されていても意味がありません。
これらは、その要素を見てみるために、とりあえず追加したものです。
今回は、これらを取り除いて、Find ボタンが押されたときに表示するように修正してみることにしましょう。
まず、初期状態では不可視にする必要があります。
要素を可視状態にするかどうかの制御には、 属性が使用されます。
プログレスメータを、 を使用して、初期状態では隠されているように変更します。
また、スクリプトから、表示したり隠したりするために参照できるように、
属性も追加しておきます。
あわせて、スプリッターと結果ツリーも、表示する必要があるのは検索実行後だけなので、それらも隠しておくことにしましょう。
id
<tree id="results" hidden="true" flex="1">
.
.
.
<splitter id="splitbar" resizeafter="grow" hidden="true"/>
<hbox>
<progressmeter id="progmeter" value="50%"
style="margin: 4px;" hidden="true"/>
属性が追加され、値は
true
に設定されています。
これによって、要素は最初に表示されたときには隠されるようになります。
続いて、Find ボタンが押されたときに呼ばれる関数の追加に取りかかりましょう。
このスクリプトは、別のファイル <tt>findfile.js</tt> に置くことにします。
これまでのセクションで、XUL ファイルに
要素が追加されていると思います。
もし、まだ追加していないのであれば、以下のように追加して下さい。 また、script
ハンドラの Find ボタンへの追加も行っておきます。
oncommand
<script src="findfile.js"/>
.
.
.
<button id="find-button" label="Find"
oncommand="doFind();"/>
それでは、<tt>findfile.js</tt> という名前のファイルを、<tt>findfile.xul</tt> と同じディレクトリに作成してください。
作成したファイルに doFind()
関数を追加します。
なお、
タグによって、コードを直接含めることも可能ではありますが、いくつかの理由によって、外部から読み込む方がパフォーマンスが高くなります。
このため、スクリプトは、イベントハンドラに直接置かれる短い断片を除いて、常に別のファイルに置くべきだと思います。
script
function doFind(){
var meter = document.getElementById('progmeter');
meter.hidden = false;
}
この関数は、まずプログレスメータの参照を、id である progmeter
を使用して取得します。
そして、次の行で、 属性の状態を変えるため、要素は再び可視状態になります。
最後に、アラートダイアログをポップアップして検索対象を表示するようにしておきます。 もちろん、これは完成版で実現したい挙動ではありませんが、とりあえずは確認のために、起こるはずの動作を表示するように追加しておくことにします。
function doFind(){
var meter=document.getElementById('progmeter');
meter.hidden = false;
var searchtext=document.getElementById('find-text').value;
alert("Searching for \"" + searchtext + "\"");
}
これで、アラートダイアログによって、Find ボタンをクリックしたときに何が起きるはずなのかが分かるようになります。 ここでは割愛しますが、ドロップダウンリストから、選択内容を取得するようなコードをさらに追加していくことも可能です。
var el = env.locale; ここまでのファイル検索ダイアログの例 : ソース 表示
XUL 要素と DOM
すべての XUL 要素は、それぞれ「属性」「プロパティ」「子要素」を 1 セットずつ持っています。
- 属性は、ソース (XUL ファイル) で宣言されるもので、例えば「
flex="1"
」の記述は、
属性をflex
1
という値で設定することを示します。 - プロパティは、JavaScript からドット構文を使って利用するもので、例えば、「element
.hidden
」は、要素の
プロパティを参照します。hidden
- 子要素は要素の子タグのことで、ソースでは要素内に入れ子になっています。
DOM メソッドを用いると、要素の属性、プロパティ、子要素を動的に操作することが可能です。
まず「属性とプロパティは別のものである」ことに注意するのは重要です。
名前が規定された属性があることは、必ず対応する同じ名前のプロパティがあることを意味しません。
とはいっても、多くの場合、対応するプロパティが存在するのも事実です。
例えば、ある要素の flex の値を得るには、
プロパティが使えます。
この場合、内部のコードでは、単に属性の値を返すだけですが、
他のプロパティでは、XUL はもっと複雑な計算を行っているかもしれません。
flex
下記に示すメソッドを利用して、要素の任意の属性を操作することが可能です。
- getAttribute ( name )
-
name
で指定された名前の属性の値を返します。 - hasAttribute ( name )
-
name
で指定された名前の属性が、値を持っていればtrue
を返します。 - setAttribute ( name , value )
-
name
で指定された名前の属性の値を、value
で指定された値に設定します。 - removeAttribute ( name )
-
name
で指定された名前の属性を削除します。
これらの関数を利用すれば、いつでも属性の値を取得したり変更したりすることが可能です。
例えば、
属性の値を使用するためには、以下のようなコードが利用できます。
flex
var box = document.getElementById('somebox'); var flex = box.getAttribute("flex"); var box2 = document.getElementById('anotherbox'); box2.setAttribute("flex", "2");
といっても、
属性に関しては、代わりに使うことのできる、対応するスクリプトプロパティを持っています。
これを使っても、特別な効果が追加されるわけではありませんが、少しタイピング量を少なくできます。
以下の例は、上の例と動作は同じですが、flex
プロパティを使うように変更したものです。
flex
var box = document.getElementById('somebox'); var flex = box.flex; var box2 = document.getElementById('anotherbox'); box2.flex = 2;
要素の参照を得ることができれば、その要素のプロパティを呼び出すことが可能です。
例えば、ある要素の
プロパティを得るためには、
element をその要素への参照とすると、
elementhidden
.hidden
という構文を用いることができます。
リファレンスを見たとき、リストされているプロパティのほとんどは、要素間に共通する属性に関係していることに気がつくと思います。
また、プロパティと属性で、違いがある場合ももちろんあります。
例えば、隠されている要素に対して、getAttribute("hidden")
を呼び出したときには、文字列として "true"
を返しますが、
hidden
プロパティを用いた場合は、真偽値として true
を返します。
この場合は、型変換が済んでいるため、プロパティのほうが使いやすくなっています。
それぞれの文書型で同様なのですが、XUL 要素には、HTML 要素や XML 要素と同様に、個別の要素オブジェクトが存在しています。
これら全ての XUL 要素オブジェクトは、XULElement
インターフェイスを実装しています。 正確には「XUL 要素」とは「XUL 名前空間で宣言された要素」のこと指しています。
このため、 XUL 要素は、他の XML 文書に加えられたときでも、XULElement
で規定されたインターフェイスを持ち、逆に XUL でない要素を、XUL 文書に加えてもこのインターフェイスは持ちません。
XULElement
インターフェイスは、XUL 要素に特有のプロパティやメソッドをいくつか持っています。
また、多くのプロパティやメソッドを、汎用の DOM Element インターフェイスから継承しています。
名前空間とは、要素の種別を指定する URI です。以下に例を示します。
<button xmlns="https://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/> <button xmlns="https://www.w3.org/1999/xhtml"/> <html:button xmlns:html="https://www.w3.org/1999/xhtml"/> <html:button xmlns:html="https://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
名前空間は xmlns
属性を使って指定します。
- 最初のボタンは、XUL 名前空間に置かれているので、 XUL 要素です。
- 2 番目の要素は、XHTML 名前空間が与えられているので、XHTML 要素になります。
- 3 番目の例では、接頭辞「html」が名前空間「https://www.w3.org/1999/xhtml」にマップされています。このように、コロンをつけた接頭辞構文を使って、特定の名前空間を使うこともできます。1 つのドキュメント内に名前空間をいくつか使っていて、どの名前空間かを正確にする必要があるときに、この構文を使用します。これにより、このケースでは、XHTML ボタンが作られることになります。
- 4 番目のボタンは少し紛らわしいものですが、重要なのは URI であって接頭辞ではないことを明確にするために示しています。この例は、接頭辞は「html」ですが、URI が、XUL 名前空間であるために、これは XUL ボタンであって、HTML ボタンではありません。
最後の例で示した、何が名前空間を決定するかを区別することは重要です。 実際、接頭辞に用いられる実テキストは、どの種別の要素かを特定するために影響を与えません。
DOM では、いくつかの名前空間と関連する関数を、名前空間を意識しない関数に似せるようにして提供しています。
例えば、getAttributeNS()
関数は、付加的な引数によって、特定の名前空間にある属性を指定可能であること以外は、getAttribute()
関数と類似しています。
多くの XUL 要素は、その要素に固有な独自のプロパティを持っています。 各要素で使用可能な属性やプロパティの完全なガイドには、XUL リファレンスを参照してください。
DOM の歩き方
DOM は、単一のルートノードが、複数の子を持つ形式の、ツリー構造になっています。
ルートノードへの参照は、document
の documentElement
プロパティを使って取得することが可能です。
このルートノードは常に要素ですが、ツリーの中の他のノードはそうであるとは限りません。
ツリーの中の要素は、XUL ソース中のタグと対応していますが、ツリーの中には、テキストノードや、コメントノードなど、他のタイプもいくつか見ることができます。
XUL の場合、ルート要素は、XUL 文書の
タグになります。
また、ツリーの中の各ノードには、子ノードを持っているものがあり、その子ノードにも、さらにそれぞれの子ノードを持っているものもあります。
このように、DOM はツリー構造であるため、いくつかのプロパティを使ってツリーの中を歩きまわることができます。
よく使われるメソッドを以下に示します。
window
- firstChild
- ノードの最初の子ノードへの参照です。
- lastChild
- ノードの最後の子ノードへの参照です。
- childNodes
- ノードの子のリストを保持しています。
- parentNode
- ノードの親ノードへの参照です。
- nextSibling
- ノードの兄弟ノードの順序で、次のノードへの参照です。
- previousSibling
- ノードの兄弟ノードの順序で、前のノードへの参照です。
これらのプロパティを利用して、様々な方法で文書内を歩き回ることができます。
例えば、firstChild
プロパティを使って要素の最初の子を取得し、それから nextSibling
プロパティを使って、子ノードの間を歩いていくことが可能です。
あるいは、同じことを childNodes
リストの項目を繰り返し処理することでも行えます。
なお、Mozilla では、後者のほうが効率がよくなります。
以下の例は、ルートノードの子ノードに繰り返し処理を行う方法を示しています。
var childNodes = document.documentElement.childNodes; for (var i = 0; i < childNodes.length; i++) { var child = childNodes[i]; // do something with child }
この、childNodes
変数は、文書のルート要素の子ノードを保持しています。
それから、子ノードに対して繰り返し処理するために for ループを使用し、各項目には配列のようにアクセスしています。
関連ページ: JavaScript 再入門、JavaScript リファレンス
次のセクションでは、 DOM の変更の仕方について学びます。