表示コンテンツの切り替え
多くのWebサイトの基本的なレイアウトは、上下左右にメニューやリンクなど、サイトの情報などを表示するスペースを設け、中央に記事などのコンテンツを表示する形になっています。特に、Blogやニュースなど、多くの記事を短い間隔で更新し続ける場合に有効なデザインだと思います。
こうしたボックス型のレイアウトを実現する方法にHTMLのframeset
要素がありますが、frameset
を用いる場合はフレーム用のHTMLと、フレーム表示するそれぞれのHTMLを個別に用意しなければなりません。さらにHTMLファイルが分離されるため、依存関係などの管理にも注意が必要です。次のサンプルを見てください。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="jp"> <frameset cols="20%,80%"> <frame src="left.html" name="left" /> <frame src="001.html" name="center" /> </frameset> </html>
これは、古い時代のWebから使われてきたフレームを用いたレイアウトの例です。表示するHTMLは「test.html」とは別に用意しなければなりません。左側のフレームには「left.html」ファイルを表示し、右側のフレームには「001.html」を表示します。左側のフレームはリンク項目などのツール的な機能を提供し、右側のフレームに主となるコンテンツを表示します。
しかし、この方法にはいくつかの問題があります。
例えば、記事の量が増え続け、HTMLファイルが数千にも達した後で、すべての記事の基本的な雛型を変更したくなった場合です。すべての記事の末尾に、著作権を表すCopyrightの一文を追加する必要があるとします。すべての記事のHTMLヘッダやソースのレイアウトが共通していたとしても、ファイル数が多ければ大変な作業になります。記事の数が数千であれば、置き換えツールを使ったとしても気楽な仕事ではありません。
困ったことに、雛形の変更は珍しいことではありません。HTMLからXHTMLの時代に移行したときはxml宣言を付け加える必要がありました。記事がリンクするCSSやJavaScriptの名前を変更したいといったこともあるかもしれませんし、すべての記事の上部に宣伝用のバナーを付け加えたいということもあるかもしれません。
また、フレーム分割はブラウザによって行われるため、ブラウザがフレーム分割に対応していない場合なども問題となるでしょう。技術的な制約でフレーム分割を使いたくない場合もあります。
BlogシステムやWikiなど、サーバー側スクリプトやデータベースを使うことができるWebアプリケーションであれば、スクリプトが実行時に必要な情報を集め、雛型にコンテンツを埋め込んでHTMLを返すことができます。ですが、趣味でサイトを運用したいと考えている方にとっては敷居が高いものでしょう。
Ajaxを用いれば、サーバー側スクリプトを使うことができないレンタルサーバーでも、雛型にコンテンツを埋め込むことができます。最初に、記事などのコンテンツを抜いたHTMLページの雛型を作成し、ここにJavaScriptを書きます。JavaScriptから、Ajaxの技術を用いて主となるコンテンツをダウンロードし、DOMを使ってHTMLページにダウンロードしたコンテンツを表示するという仕掛けになります。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="jp"> <head> <title>test</title> <script type="text/javascript"> function createRequest() { var req = null; if ("XMLHttpRequest" in window) { req = new XMLHttpRequest(); } else if ("ActiveXObject" in window) { try { req = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { req = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {} } } return req; } var req; function requestText(url) { req = createRequest(); req.open("GET", url, true); req.onreadystatechange = req_readystatechange; req.send(null); } function req_readystatechange() { var divRightPanel = document.getElementById("rightPanel"); if (req.readyState == 4) { if (req.status == 200) { divRightPanel.innerHTML = req.responseText; } else { divRightPanel.innerHTML = "システムエラー:読み込めませんでした"; } } } </script> </head> <body> <table border="0"> <tr><td style="width:20%" valign="top"> <p><a href="javascript:requestText('001.txt')">はじめに</a></p> <p><a href="javascript:requestText('002.txt')">Ajaxとは</a></p> </td> <td style="width:80%" valign="top"> <div id="rightPanel"></div> </td></tr> </table> </body> </html>
sample03の「test.html」は、sample02のようなレイアウトをAjaxを応用して実現したHTMLページです。このHTMLファイル自体がサイトの重要な雛型として機能し、記事などのコンテンツはリンクをクリックするとrightPanelというIDを与えているdiv
要素上に表示されます。sample02と比較して、ハイパーリンクをクリックしてもページ移動が発生しないことに注目してください。JavaScriptが内部でサーバーにファイルを要求し、ダウンロードしています。
createRequest()
関数は、Ajaxの中枢であるXMLHttpRequest
オブジェクトを生成して返します。requestText()
関数は、url
引数に指定したアドレスに要求を行います。URLは相対アドレスでもかまいません。通常、ブラウザのセキュリティによって異なるホストへのアクセスは禁止されているため、通常は相対アドレスで十分でしょう。
req_readystatechange()
は、XMLHttpRequest
オブジェクトのonreadystatechange
プロパティに設定するための関数です。この関数は、サーバーから応答があった時に呼び出されます。req_readystatechange()
関数内で、データが完全にダウンロードされたかどうかを調べ、完全にデータがダウンロードされた時点でresponseText
プロパティから単純な文字列としてダウンロードしたデータを取得しています。
HTMLページに埋め込まれる記事は、HTMLファイルとは別の「001.txt」と「002.txt」ファイルに用意しています。例えば、「001.txt」ファイルの内容は次のようになっています。
<h1>はじめに</h1> <p> ここ最近、Web 2.0という言葉と共にクライアント側の...(略) </p> <p> では、Ajaxはそれほど敷居の高いものなのでしょうか。 質の高い開発集団や、高額な開発環境、またはサーバーなどの...(略) </p>
本文は長くなるため省略していますが、HTMLの部品となるテキストであることが確認できます。HTML要素やヘッダなどの情報が含まれていないため、これ自体は不完全なHTMLです。「test.html」ファイルのスクリプトに読み込まれ、div
要素のrightPanelに埋め込まれることが前提となっています。
この構造であれば、記事の本文をHTMLの全体の構造と切り離すことができ、柔軟にページのレイアウトを変更することができます。
設定ファイル
sample03は、XMLHttpRequest
オブジェクトを使ったスクリプト内部によるダウンロードと、DOMによるHTMLの構造の動的な変更という、Ajaxの基礎的な技術を応用したサンプルです。しかし、数千の記事を扱うサイトの場合はリンクの部分が大きな問題となります。
sample03は、HTMLのbody
要素内に直接a
要素を書いています。これは、参照する記事が2つだけだったので手書きで十分でしたが、数千という記事を参照するリンクを何の構造化を行うこともなく、手書きで用意するのは非効率的です。
こうした問題を解決するには、参照するファイルの情報などを記した設定ファイルを用意するといった方法があるでしょう。設定ファイルは、HTMLソースとは分離して用意し、XMLHttpRequest
でダウンロードして使います。例えば、設定ファイルにサイトマップなどの情報を書き込み、スクリプトが設定情報を分析してリンクを含むHTMLに変換するという方法があります。
設定ファイルのフォーマットはXMLが最適です。XMLHttpRequest
オブジェクトには、ダウンロードしたデータをDOMで定められているDocument
オブジェクトとして返すresponseXML
プロパティがあります。Document
オブジェクトの詳細はDocument Object Model Specificationsを参照してください。
設定ファイルをXML形式で記述し、JavaScript+DOMでデータを読み込む方法であれば、設定情報を解析する専用のパーサーを開発する必要はありません。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="jp"> <head> <title>test</title> <script type="text/javascript"> function createRequest() { var req = null; if ("XMLHttpRequest" in window) { req = new XMLHttpRequest(); } else if ("ActiveXObject" in window) { try { req = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { req = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {} } } return req; } var req; function init() { var divLeftPanel = document.getElementById("leftPanel"); req = createRequest(); req.open("GET", "setup.xml", false); req.send(null); var doc = req.responseXML; var setup = doc.getElementsByTagName("setup")[0]; var docsList = setup.childNodes; for(var i = 0 ; i < docsList.length ; i++) { var docs = docsList.item(i); if (docs.nodeType == 1 && docs.nodeName == "documents") { var from = docs.getAttribute("from"); var to = docs.getAttribute("to"); var prefix = docs.getAttribute("prefix"); var suffix = docs.getAttribute("suffix"); var title = docs.getAttribute("title"); divLeftPanel.innerHTML += "<p>【" + title + "】<br/>"; for(var j = from ; j <= to ; j++) { divLeftPanel.innerHTML += "<a href=\"javascript:requestText('" + prefix + j + suffix + "')\">" + j + "</a> "; } divLeftPanel.innerHTML += "</p>"; } } } function requestText(url) { req = createRequest(); req.open("GET", url, true); req.onreadystatechange = req_readystatechange; req.send(null); } function req_readystatechange() { var divRightPanel = document.getElementById("rightPanel"); if (req.readyState == 4) { if (req.status == 200) { divRightPanel.innerHTML = req.responseText; } else { divRightPanel.innerHTML = "システムエラー:読み込めませんでした"; } } } </script> </head> <body onload="init()"> <table border="0"> <tr><td style="width:20%" valign="top"> <div id="leftPanel"></div> </td> <td style="width:80%" valign="top"> <div id="rightPanel"></div> </td></tr> </table> </body> </html>
<?xml version="1.0" encoding="UTF-8"?> <setup> <documents from="0" to="1" prefix="ajax" suffix=".txt" title="Ajax入門" /> <documents from="0" to="4 prefix="c" suffix=".txt" title="C入門" /> </setup>
sample04は、sample03をさらに発展させ、左側の領域に表示されるリンクの生成を自動化させたものです。sample03で左側に表示されているハイパーリンクはHTMLソースに直接書き込まれていたものですが、sample04は「setup.xml」に書かれている情報からスクリプトに自動生成させています。
「setup.xml」は、設定情報を格納するXMLファイルです。この設定情報のルート要素にはsetup
を指定するものとし、setupの子要素には記事などの連続的なファイル情報をまとめるdocuments
要素を指定するものとします。documents
要素には、開始番号をfrom
属性に、終了番号をto
属性に指定します。連続的な記事の主題をtitle
属性に指定するものとします。
prefix
にはファイル接頭辞、suffix
には接尾辞を指定します。例えば、「article0.txt」~「article20.txt」までの連続的な記事がある場合は、from
に0、to
に20、prefix
にarticle、suffix
に.txtを指定することになります。
<documents from="0" to="20"
prefix="article" suffix=".txt" title="記事の名前" />
スクリプトでは、これらの属性情報を受け取ってprefix+ファイル番号+suffixという形で参照するURLを作っています。
コンテンツの管理を行い、リンクなども自動生成してくれるサーバー側スクリプトに比べると、設定ファイルを書くという作業が余分に発生していますが、リンクのすべてをHTMLファイルで管理することに比べれば、こうしたAjaxを応用した手法で簡略化する方法も選択の1つかと思います。