DOMとは、w3cによって策定された、プログラムからxml(html)ドキュメントに、アクセスするためのインターフェイスです。このインターフェイスは、特定のベンダーに依存することはありません。また、プログラム言語から、独立した形で仕様が決められているため、どのような言語にでも実装できるようになっています。
DOMが、プログラム言語からは、独立した形で仕様が決められていると書きましたが、DOMの仕様には、JavaとECMAScriptのAPIが、付録という形で載っています(ECMAScriptとは、JavaScriptの現時点の標準仕様のことで、一般的なWebブラウザはこの仕様に則って、JavaScriptを実装しています)。このあたりが浸透すれば、JavaScriptでも、互換性の不安無く、DOMを使える環境が整いつつあります(いつかはきっと・・・・)。
DOMの守備範囲は、あくまでもxml(html)へのアクセスです。タグの表示スタイルを変更したり、特定のIDが設定されているタグを取得したりすることは、仕様の範囲内ですが、新しいウィンドウを開いたり、マウスカーソルを移動したりといった、xml(html)ドキュメントへのアクセス以外のことは、仕様の範囲外となります。
JavaScriptでDOMを使う場合、何はともあれdocmentオブジェクトを取得する必要があります。但し、DOMの仕様では、docmentオブジェクトを取得する方法は、定められてはいません。これは各実装者(例えば、Webブラウザの作成者)が、勝手に決めることを意味しています。幸いにも、htmlの中に記述またはリンクされたJavaScriptでは、docmentオブジェクトは、すでに用意されているので、特に取得する必要はありません。
以下の例では、ドキュメントに、"Hello World"と表示します。
document.writeln('Hello World');
DOMで何かをする場合、対象となる要素を見つけなければなりません。このためのメソッドが、docmentオブジェクトにはいくつか用意されています。代表的な物を以下にあげます。
Element getElementById(in DOMString elementId); NodeList getElementsByName(in DOMString elementName); NodeList getElementsByTagName(in DOMString tagname);
getElementByIdは、ドキュメント内から、特定の id が設定してあるエレメントを探します。html4 の仕様上、id はドキュメント内でユニークであることから、このメソッドは、単一のエレメントを返します。
getElementsByNameは、ドキュメント内から、特定の name が設定してあるエレメントを探します。html4 の仕様上、複数のタグに同一の name が設定できることから、このメソッドは、複数のエレメントを含むリスト(配列)を返します。
getElementsByTagNameは、ドキュメント内から、特定の名前のタグ探します。このメソッドは、複数のエレメントを含むリスト(配列)を返します。
<input type="text" name="column0" size="20" id="row0column0"> <input type="text" name="column1" size="3" id="row0column1"> <input type="text" name="column2" size="10" id="row0column2"><br> <input type="text" name="column0" size="20" id="row1column0"> <input type="text" name="column1" size="3" id="row1column1"> <input type="text" name="column2" size="10" id="row1column2"><br> <input type="text" name="column0" size="20" id="row2column0"> <input type="text" name="column1" size="3" id="row2column1"> <input type="text" name="column2" size="10" id="row2column2"><br> <input type="text" name="column0" size="20" id="row3column0"> <input type="text" name="column1" size="3" id="row3column1"> <input type="text" name="column2" size="10" id="row3column2"><br>
上記のようなhtmlドキュメントに対し、
var item = document.getElementById('row1column2');
を実行すると、itemには、2行目3列目のinputエレメントが格納されます。
同様に、
var items = document.getElementsByName('column1');
を実行すると、itemsには、2列目のinputエレメントが4つ格納されます。
var items = document.getElementsByTagName('input');
の場合は、itemsには、全てのinputエレメントが格納されます。
エレメントさえ見つけてしまえば、後は好きなように料理できます。サイズ・色・表示/非表示なども、簡単に変更できます。そう、スタイルを変えてしまえば良いのです。
下記の例では、name というIDを持ったエレメントの背景を、赤に変更します。
document.getElementById('name').style.backgroundColor = 'red';
エレメントオブジェクトのstyle属性は、"DOM Level 2 Style Specification" で定義された、CSSStyleDeclarationオブジェクトです。アプリケーションからは、このオブジェクトを通して、各エレメントの、インラインスタイルにアクセスすることができます。
CSSStyleDeclarationオブジェクトからアクセスできるスタイルは、CSS2Propertiesに定義されている項目のうち、該当エレメントに設定可能なものだけです。スタイルを設定できないエレメント(例えばtitleエレメント)のスタイルを変更したり、CSS2Propertiesに定義されていない項目を変更しようとすると、エラーになります。
ご参考までに、CSS2PropertiesのIDL定義(w3cのサイトへのリンクです。)
上記の方法は、個々のタグに対して、スタイルを変更するものでした。しかし、状況によっては、複数のタグの内容を、一度に変更したい場合が有ります。
このような場合、スタイルシートのセレクタ毎に、スタイルの内容を設定することができます。
IDLは、以下のようになります。
interface DocumentStyle { readonly attribute StyleSheetList styleSheets; };
インターフェイス、DocumentStyleは、documentオブジェクトに実装されているので、documentオブジェクトから、直接styleSheetsアトリビュートを参照できます。
interface StyleSheetList { readonly attribute unsigned long length; StyleSheet item(in unsigned long index); };
itemメソッドは、定義されているスタイルシートがCSSの場合、CSSStyleSheetを返します。
interface CSSStyleSheet : stylesheets::StyleSheet { -- 省略 -- readonly attribute CSSRuleList cssRules; -- 省略 -- };
interface CSSRuleList { readonly attribute unsigned long length; CSSRule item(in unsigned long index); };
interface CSSRule { // RuleType const unsigned short UNKNOWN_RULE = 0; const unsigned short STYLE_RULE = 1; const unsigned short CHARSET_RULE = 2; const unsigned short IMPORT_RULE = 3; const unsigned short MEDIA_RULE = 4; const unsigned short FONT_FACE_RULE = 5; const unsigned short PAGE_RULE = 6; readonly attribute unsigned short type; attribute DOMString cssText; // raises(DOMException) on setting readonly attribute CSSStyleSheet parentStyleSheet; readonly attribute CSSRule parentRule; };
interface CSSStyleRule : CSSRule { attribute DOMString selectorText; // raises(DOMException) on setting readonly attribute CSSStyleDeclaration style; };
まず、ドキュメントオブジェクトから、スタイルシートのリストを取得します。このリストの中には、ドキュメントに対して、linkエレメントでリンクされたスタイルシートや、stylesheetエレメントで指定されたスタイルシートが含まれます。
次に、スタイルシートの中から、必要なCSSRuleオブジェクトを検索します。このとき、セレクタ名称などで検索する方法は提供されないので、自分で用意することになります。
必要なCSSRuleオブジェクトさえ見つければ、後は特定のタグのスタイルを変更する場合と、大差有りません。但し、直接スタイルを変更できる、CSSStyleDeclarationオブジェクトを持っているのは、RuleTypeがSTYLE_RULE・FONT_FACE_RULE・PAGE_RULEである、CSSRuleオブジェクトだけである点に注意が必要です。
また、同じセレクタに対し、複数のスタイルを設定している場合(カスケードしている場合)、当然同じセレクタ名称が複数見つかる事にも、注意する必要が有るでしょう。
下の例では、日本語の部分に.jp英語の部分に.enをクラスで設定しておき、全ての英語部分を非表示、全ての日本語部分を表示にしています。どのような記述のスタイルシートにも対応させるためには、CSSRuleオブジェクトのselectorTextアトリビュートを判定する前に、RuleTypeがSTYLE_RULEかPAGE_RULEである事を、チェックしなければなりません。何故なら、それ以外のRuleTypeの場合、selectorTextアトリビュートが存在しないからです。
非常に強力な機能ですが、残念ながらIE6以下では動作しません。
var styleList = document.styleSheets; for (var i = 0; i < styleList.length; i ++) { var styleSheet = styleList.item(i); var cssRuleList = styleSheet.cssRules; for (var j = 0; j < cssRuleList.length; j ++) { var cssRuleItem = cssRuleList.item(j); /* * 初期表示は、日本語 */ if (cssRuleItem.selectorText == '.en') { cssRuleItem.style.display = 'none'; } else if (cssRuleItem.selectorText == '.jp') { cssRuleItem.style.display = ''; } } }
通常Webアプリケーションの、クライアント・サーバ間の通信は、クライアントからのリクエストに対して、サーバが次ページのデータを、全てレスポンスするというのが、一般的です。
同一の画面から、データを次々に入力したいような場合、これはやっかいです。また、キーのみサーバに送って、検索結果を表示したいような場合も、本来なら検索結果だけをレスポンスすれば良いはずですが、実際には表示するページ全体を、レスポンスしています。
JSPを普通に使うと、このような形になってしまうのですが、パフォーマンスの面で問題があることは、明らかです。特に最近のWebアプリケーションの画面が、デザイン的にも凝ったものになってきていることを考えると、この問題は大きくなるかもしれません。
比較的遠い将来を見据えれば、"DOM Level 3 Load and Save"あたりが、解決策になるのかもしれませんが、本当に使える様になるのは2〜3年後でしょうから、まだまだ先の話です。
(注)"DOM Level 3 Load and Save"が、本当に解決策になるのかどうかは、分かりません。そもそも"DOM Level 3 Load and Save"が、何のために策定された(る)仕様なのか、私自身がはっきり知らないのですから、問題外です(笑)。baseURIから、Streamを使ってドキュメントを読み書きする仕様のようなので、「そんなもんかなあ」と想像した訳で・・・・。
当サイトの例では、サーバからのレスポンスを、別フレーム(表示されないbufferフレーム)に読み込み、ロード完了のイベントで、メインとなるフレームの画面に、内容を表示しています。インターネット上でも、それほどストレス無くレスポンスされてくるので、今後のプロジェクトでIE5.5以上のみをターゲットにしたものが有れば、試してみようと思っているものです。
この方法には、もう一つうれしい副作用があります。メインとなる画面を、サーバロジックと完全に切り離した、HTMLだけで作成できると云うことです。通常JSPでは、HTMLにjavaのロジックが多少なりとも記述されるため、それだけで完全な形の表示をすることは難しいのですが、HTMLだけで記述されていれば、そのようなことはありません。
また画面遷移も、HTMLだけで記述されていれば、簡単にクライアントだけで実現できます。まずクライアントをプロトタイプの様に先行し、使用感等を先に確認をしてもらって、その間にサーバの実装をする。できそうでできないこんな事も、簡単にできます(たぶん)。
クライアントの画面と、サーバ側のロジックとの接点は、HTMLの各エレメントに設定されたidのみ。という事が実現できれば、プロジェクトの自由度が(設計もスケジュールも)少しだけ広がる気がします。
まあ、JSPだけで実現するプロジェクトよりは結構大変で、クライアントをアプレットで作るプロジェクトよりは、かなり楽という感じでしょうか。