HTMLページの構造の階梯(かいてい)

Column

CSSの解説などでよく、内容と形式の分離がどうこう、というごたくが並べられるのであるが、表現される内容(シニフィエ:例「強調」)と、表現手段(形式、シニファン:例「太字」)の区別は、実際には、相対的なものにすぎない。

ありがちなレベルの説明

まず0階の例として、HTMLドキュメント内にすべてをベタで書くと、こんなことになるであろう。

<div align="right"><address>
<font face="Times New Roman" size="2" color="green"><i>(c) example.com, 2001.<br>
E-mail: info@example.com</i></font>
</address></div>

これは、<address> として、example.com という著作者名とメールアドレスを記載している例である。このレベルでは、「(c) example.com,2001. E-mail:...」というのが「内容」であって、「文は右寄せのレイアウト、Times New Roman フォントのフォントサイズ2の緑色の文字のイタリック」というのが「形式」にあたる。表示したいテキストと、テキストの表示法に関する指示が入り乱れていて可読性が悪い。このレベルの構造と形式は、CSSによって分離される。

<address>
(c) example.com, 2001.<br>
E-mail: info@example.com
</address>

address {
text-align: right;
font: italic small "Times New Roman", serif;
color: green;
}

HTML内には <address> として実際に表示される文字列のみを記述し、その文字列の表示のされ方は、外部のCSSに書く――この方法には現実的には欠点もあるが、理論的に非常にべんりであることは、ご承知の通りである。このレベルでの「形式」例えば、フォントの色を緑から灰色に変えたくなったとき、第一の方法では、ぜんぶのページの address 表示の color属性を書き換えなければいけないが、第二の方法だと、CSS の color: を一か所なおすだけでそのCSSを読み込んでいる全ページに変更が反映される。各ページで同じ「アドレス欄の表示の仕方」の指示を繰り返すより、全体のテキスト量も少なくなるだろう。

それより上の構造――サイト全体=1メソッドへ至る階梯

しかし、この第一レベルでは、各ページに依然、同じアドレス表示が実体でナマのまま残っているから、上記とまったく同じ意味でムダがある。例えば「2001」を「2001, 2002」に変更したくなったとき、全ページを書き換えなければならないのは、めんどうだ。そこで2階の構造化として、

<?
    print_address();
?>

‥‥と書いてみる。print_address() の内容は、各ページで共有的にインクルードされる外部の PHP に依存する。ASPでもCFMでも概念的には同じだ。CGI で同じライブラリを読み込むと考えても良いし、このレベルではSSIのコマンドだと考えても良い。

以下、視点を変えるごとに、「形式(シニファン)」と「内容(シニフィエ)」の意味がめまぐるしく変化するので、混乱しないように注意してほしい。

今は、まだ、HTMLレベルと事実上、一対一対応でものを考えている。さらに3階の構造化として、

<?
    print_footer();
?>

‥‥を考える。ここでは上記の「print_address()」は、もはやナマの実体にすぎない。各ページの下部にプリントすべき内容、つまり「フッタ」という高レベルの構造の一部として呼び出される「内容」だ。大規模なサイトは、しばしばこんなふうに管理されているはずだ。ここまで抽象化しておくと、ページによって、print_address を呼び出す位置を変えたり、ページによってシステマティックにナビゲーションリンクを生成できる。

「次のページへ」「前のページへ」からなるナビゲーションリンクは、ページの番号が1ずつ変わるほかは各ページで完全に共通なのがふつうであるから、各ページごとにベタで書くのは効率が悪い。動的に関数で作れば、次のページのページ番号をミスタイプしたりすることもなくなる。

もちろん、print_footer の中には、</body></html> のようなお決まりの出力処理も含まれる。

ヘッダも動的に生成すれば、/de/ というディレクトリにある文章に対しては、自動的にドイツ語のフォントに関するCSSを動的にインポートし、/ja/ に対しては日本語のフォントに関するCSSをインポートし‥‥といったことや、ユーザエイジェントによってまったく違うプレゼンテーションをサーバサイドで構築することができる。

これでもまだ満足できない場合は、ボディも動的に生成すれば良い。全ページを同じ内容、すなわち、インクルードライブラリの呼び出しと、print_html(); という関数だけにして、「print_html」という構造の内容物が「print_header, print_body, print_footer」だとすれば良い。

print_html は、自分自身の URIや環境変数から自分がどんな文書を生成すべきかを判断して(あるいはデータベースに問い合わせて)、アクセスすべきテキストファイルからテキストを読み込み、自分で自分にマークアップをほどこし、必要なCSSやJSをインポートする。いまどきのサイトだと、このレベルの構造化も珍しくないはずだ。例えば、show.asp?page=hoge のようなURLでページを見せている場合、実装にもよるが、だいたいこういう発想だろう。――これらはサーバサイドで処理されるため、ユーザサイドでアクティブスクリプトが正しく動いてくれる、という心もとない仮定をする必要もない。――

最終的には、サイト全体が、ひとつのスクリプトとURIの引数による仮想ページの集まりになる。

こうして構造の階梯を考えると、HTML3.2に対する HTML4.01 strict あたりで「構造と形式の分離」というのは、相対的な問題――無視できない量ではあるが、わずかな量の違い――にすぎないことが実感されるであろう。

「べんり」の意味

次の2点に注意しよう。

HTML3.2 に対して、HTML4.01+CSS は、たしかに表現力が大きく、柔軟でべんりであるが、その良さをある程度、実感するには、HTML3.2 で低水準の仕事をすることも必要かもしれない。文中に強調語句があらわれるたびに、

<font color="#ff0000"><b>柔軟な発想が最も大切</b></font>だ。

‥‥などと、色の指定と太字の指定を何度も何度もタイプするのは、なんかめんどくさいなぁと、いったような経験があるかたなら、「じつは、全部 <strong> とだけ書いておいて、別の場所でいっかつして strong{color: red; font-weight: bold;} と指示するのでも同じことになる」と聞けば「それは耳よりな話!」と反応できるだろう。

同様に、<h2 class="date"> だの <h2 class="subject"> だの毎日、書いている日記著者のかたなら、<date> とか <subject> というユーザ定義タグというか速記法が使えるXMLを、魅力的と思うだろう。

さらにまた、ある程度の規模のサイトを管理する苦労を知っている者なら、ページを部品に分解して、モジュールを include することのありがたみがひしひしと分かる。

このように、あることがらの「べんりさ」が実感できるには、そのことがないと、どんなに大変か、という経験が必要だろう。

ひるがえって、これから初めてHTMLを勉強しようという右も左も分からない相手に、「こんにちのHTMLでは構造と形式を分離しなければならないのである。なぜならそのほうがべんりで正しいのである。構造とはこれこれで形式とはこれこれのことである。であるから、fontというタグは使っては、いけません」なんて告げてみても、実感として理解してもらえるかどうか分からない。言っている内容が正しいか否かでなく、教育の問題、勉強する側からみれば学習の問題だ。こんなジョークがある。「新しい数学教育は完全に成功した! 小学生に 3+4 はいくつか尋ねたら、相手は立派な答をした――いわく『加法は可換だから 4+3 に等しい』」

C++のオブジェクト指向、構造化プログラミングを考えても、不用意にグローバル(パブリック)変数を使いまくってどこかわけの分からないところで勝手にグローバル変数の値が変更されてしまってバグがとれず悩みまくって頭がウニになる経験があってこそ、「これは、おいらのオブジェクトが内部的に使うプライベート変数だ。ほかのモジュールから勝手にいじらせないぞ」というアクセス制限の良さが分かるし、変数の初期化を忘れてひどい目にあっていればこそ生成と同時に自動でデフォルト値がセットされることのありがたみも分かるであろう。

そうした経験がない者には、オブジェクト指向のどこが良いのやら、なかなか実感できないのでは、ないか。かえって単純な物事を複雑にするだけだ、と見えるのでは、ないか。さらにまた、構造化も唯一の正しさでなく、やりすぎると、逆効果になることは、ご承知の通りである。1つのファイルに、main にあたる Do とかいう関数を書いて、べつのファイルで Do とは、init, run, done のことだと定義して、またまたべつのファイルで init とは initA と initB のことで‥‥などと際限なくトゥリー状に書くのは「非常に構造的で、どこかをなおしたいとき最小限の手間で再コンパイルできる」とは言うものの、通常は望まれないであろう。いくら人間の脳があほうで数千、数万の変数を同時に考察できないと言っても、5つか6つくらいの識別子は同時に意識できるハズだから、ぜんぶをバラバラにして構造を析出しつくすより、ベタで書いても分かる範囲はベタで書いたほうが分かりやすい。

ただ、たしかに、記事単位のデータにしても、日付、題名、本文などをべつべつのファイルで管理しておけば、題名だけ修正したいときに本文を再転送する必要がなくなる、といったメリットもある。

注意その2

数学(とくに集合論や基礎論などの公理論的な手法)とあまり縁のないかたは、構造化という概念そのものが新鮮でおもしろいあまりに、CSSの「構造化」の美しさに酔ってしまうかもしれない。構造化は、たしかに物事の見通しを良くして美しいものだし、その美しさを感じてうっとりするのも好ましいことだ。

しかし、酔うとほかの見方ができなくなる、という可能性に注意する。

一度、徹底的にやって「ガチガチの構造化を突き進めると、現実的でなくなることが多い」というのを体験するのも重要かもしれない。XMLは、ほかのXMLやHTMLなど、多くのフォーマットに変換できるメタとして、完全なツリー構造を入れる。不特定の者がいろいろなフォーマットに変換しなければならないときには、最適のソリューションのひとつだろう。が、HTMLだけに変換すると決まっているならしばしば冗長である。たとえ話的にいえば、int と書いてしまうと int が 32ビット幅でない環境でうまくないので、INT というマクロで書いておく――みたいなもので、実際には自分のその環境でしか走らせないのなら、こういうマクロは冗長なだけだ。

XMLを内部的に実際のHTMLに変換する指示の出し方は、現状、けっこう特殊と言わざるを得ない。この方法は、クライアントサイドで実行できるかわり、世界中のクライアントサイドでいかようにも変換できるように、面倒な書き方になっている。サーバリソースを度外視すれば「自分でPerlあたりでパーサを書いたほうが効率的」と思うかたもおられるだろう。

date: 2001.09.11
title: HTMLページの構造の階梯(かいてい)
text: /home/data/2001/0911.txt

例えば、上のような「My ML」、閉じるタグなんか要らない。ページを変更するときは、そのページに読み込むデータの参照だけ変更すれば良く、読み込まれるデータの実体そのものを書く必要は、ない。そのデータがいわゆる構造化言語で書かれているかどうか、というレベルと違うレベルで、読み込まれるテキストは単なる生の素材――「構造」でなく「構造のなかにそそぎ込まれるナマモノ」――とみる。パースすると、ほしいHTMLが動的に生成される。例えば、古いブラウザで読むと、

<h2>2001.09.11</h2>
<h3><a name="d10911">HTMLページの構造の階梯(かいてい)</a></h3>
<p>本文を読み込んで、改行ごとにパラグラフとする‥‥

UAが新しければ、同じものを次のように変換することでほんの少しファイルを圧縮できる。(UA変数はユーザ側で書き換えられるので、UA変数を信用するのは安全でない)

<h2>2001.09.11</h2>
<h3 id="d10911">HTMLページの構造の階梯(かいてい)</h3>
<p>本文を読み込んで、改行ごとにパラグラフとする‥‥

望むなら、XHTML や XML で出力してもいい。

実際には、データファイルのフォーマットを決めておけば、日付と題名も勝手に判断してくれるだろう。例えば、そのページが30KBのテキスト量を超えたら、そこでうち切ることにして、アーカイブ全体を動的かつ自動的に保守することも可能だ。そうしたければ、一週間以上古い記事は自動的に表紙から消えるようにしたり、定期的に外部へのリンクを connect して、つながらないリンクは自動的に消す(ただのテキストにする)と同時に、管理者にリンク切れ報告メールを自動発送する、なんてことも考えられる。

他者とメタレベルで文書を共有する必要がなければ、これも最適のソリューションのひとつだろう。言い換えれば、XMLは直接的に「異なるプレゼンテーション層にリアライズされるひとつのメタデータ」を交換する場合に威力を発揮する。自分用のHTMLにしか変換しない、将来HTML5やHTML6というのができても、自分でパーサを調整すればいいし、どうしてもXMLが必要になったら、My ML を XMLに変換させることもできる、というような場合には、わざわざ汎用性、移植性が高いXMLで書かなくても、自分が分かる中間言語で書いておけば良い。むしろ「今の最新XML仕様書」にあわせて書いても、そのマークアップは3年後には事実上、時代遅れになるかもしれない。

つきつめると元に戻る部分も

徹底的に構造と内容を分離するなら、そのページには構造のテンプレートのみを記述すべきであって、ナマのテキストを直接タイプすべきでない。

html_head();
include_articles("39-24001", "39-24010");
html_foot();

こうなると、もうページのファイルを実際に用意するより、/?lookat=39-240 のようにURIだけで仮想的なページにしてしまうほうが速い。構造と内容を本当に分離すると、構造というのは目に見えないものなのだから、ページの実体は存在しなくなる。URIの参照に応じて、メソッドで動的に生成される。

言い換えると、「あるURIで表されるページに、そのページで表示されるべき文章が実体で直接記述されているとしたら、構造と内容がきちんと分離されていない」とも言える――実際、画像などは、ページ内に直接は書かれずに参照されることが多い(小さな画像ならCGIの中に直接16進数で書くこともできる)――。これは通常の感覚に照らすと極論かもしれないし、べつに上の意味でテンプレートとコンテンツを分離するのが常に正しいと言いたいわけでない。要するに「構造を分離する」ことが第一優先の重要問題でもない、ということだ。

けれど、ドキュメントの内部にメソッドを埋め込めること(ColdFusionやPHP)、処理の指令と処理の対象を1ページにまぜてしまうことで、かえって保守性が良くなることもあるのは、経験者にとっては明白であろう。いくつかのプログラミング言語で、動詞(メソッド)と名詞(プロパティ)が対等のメンバになるのと似ている。次の3段階の視点を区別しよう。

  1. たまにちょっと色を変えたりする程度なので、素朴に font タグ等を使ってマークアップする、という立場。
  2. 処理すべき対象(テキスト)と、その対象にほどこす処理メソッドをまぜこぜに書くのは良くないので、抽象化して分離する、という考え方。
  3. さらに高いレベルの抽象化。――処理すべき対象とメソッドを故意に並列してしまうレベルの抽象化には、好ましいこと『も』ある、という視点。例えば、ある対象に対して、直接 style= と指示すれば、この style 指定は、そのオブジェクトのみに作用するプライベートなメソッドであって、外からはアクセスできない。このようなことは、ほかの位置(外部)に書くCSSでは、ほぼ不可能だ。id 属性を使っても、ファイル全体からアクセス可能なスコープに入ってしまう(そのことに潜在的な危険性を感じるほど高度に複雑なページを作る者は少ないとしても)。そのオブジェクト固有のスタイルでほかで反復されないなら、外部ファイルに記述するのはムダな面もある。みだりにべたで style を書くのは、美学的観点からは、どうかと思うが、それなりのメリットもあるし、かえって可読性が高まる場合もある。

トラフィックについて言えば、CSSやJSの外部ファイルは通常、充分に小さくて、キャッシュされても利益は少ない。本当は、すべて bzip2 とかで送って、ブラウザ側で透過的に即時解凍してくれればテキストは約20倍の速度で転送でき、64Kbpsの人でもテキスト系のページを 1Mbps 相当で見れる。

すぎたるはなお‥‥

ブルバキ『数学原論』ですら、冒頭から「過度に形式的にやると逆効果だから、分かる言葉は適当に省略したりする」と断っている。

プログラミングでも、Cで育った者がPerlなどやると、必要ないのに、my $a と宣言したあとで = ""; と書くかもしれない。JavaScript で長さ10の配列を作ったら、そのそばから、全部の要素を0で初期化しないと不安かもしれない。この気持ちは分かる者には、いやというほど分かるであろう。が、Cとは無縁のPerlプログラマからみると、こいつは何、ムダなことばかり書いてコード量を倍増させやがるんだ、となる。どちらの感覚も、その立場に立てば、もっともなものだろう。

同様に、HTML3.2 と HTML4.01+CSS は異なる言語であるから、一方から他方を見ると、「そんなややこしく不確実な(ブラウザ依存の)ことをしなくても、テーブルで書けば、きちんと二次元のレイアウトができますよ」と思うし、他方から一方を見ると、「表形式の構造じゃないのになぜテーブルを使う。W3Cの神さまにざんげしなさいっ」と思うであろう。

「数学の証明とは図である」とか「このことは実数に限らず、任意の数体のうえで成り立つ」といったたぐいの抽象化からみれば、HTML/CSS/XML あたりは、結局、同じようなものに思えるかもしれないが、そういったたぐいとこれまで無縁だったかたにとっては、font のマークアップですら「非常に形式的で、難しい」かもしれない。実際、「文字のおおきさ」という「内容」が、
<font size=""> ... </font>
という初めてみれば難しげな「構造」に形式化されているのであって、font タグも、あるレベルでみれば、文字そのものの実体と「文字の大きさ」という形式を分離している。

現実的な適応が低くても、理論的に正しければ気持ちいいと思うかたもおられるであろうが、たいていは現実と仕様のギャップを考慮するだろう。そのさい、後方互換を優先して新しい仕様を使わないという立場と、ある程度、後方互換を犠牲にしてでも新しい仕様を使うという立場の使い分けが、TPOによって、非常に複雑になってくる。いずれにせよ、我々は利用可能なすべての表現手段を利用可能なのであるし、たいていの者は、最新の仕様書に厳密に従って書く形式美の快感――それもよく分かるが――より、要するに手っ取り早くコンテンツを作成できて保守できる、ということを望むであろう。

大規模コンテンツの保守性を高めるには、あらかじめ小規模で実験して、充分に計算して設計しなければならず、そうでない場合は、古いコンテンツを保守しやすいコンテンツに変更するために苦労するであろう。未来の保守性のメリットと、それを得るために全体を変更しなければならないというさしあたってのデメリットのあいだには、どこかに損益分岐点があるはずだ。また、HTML→CSS→XML→PHP→CGIのように、より高レベルでラクに大規模管理を行おうとすればするほど、管理者がラクをするぶん、HTMLを生成するのにマシンリソースを必要とするだろう。初めから出力されるHTMLをべたで書いて構造に関しては外部ファイルに依存しなければ、その1ページに関する限りは、最も高速になる。ある意味、アセンブラとIDE、高レベルのクラスライブラリの違いにも似ている。実際、CGIで書くということは、ページを出力するごとにPerlのスクリプト(高級言語)がコンパイルされて、それが動いて、処理をして、またすべて解放する、という繰り返しになる。

スタティックなHTMLを書くのに慣れきっていると、HTMLとは、そういうものだという錯覚におちいり、サーバから送られてくるHTMLのソースを眺めて文法的に「正しい」だの「正しくない」だの、その一点にこだわりがちだが、ちょうどページのソースという概念を知らずに、ページの見た目がクールできれいだの、パステルカラーで素敵だの言うのに似ている。「ブラウザに送られるtext/htmlファイル」は――最終的にUAに送られる出力だから重要だけれど――上記のような構造の階梯においては、表面的な「見た目のレイヤ」だ。どういうソースを書くとこう表示されるのか?というのがHTMLの学習者にとって興味深いように、ウェブの管理者は、どういうスクリプトを使うと、たくさんのページをラクに間違いなく保守できるかに興味がある。ウェブページの書き方については、主なパターンとその長所短所は誰もが実際にいろんなサイトを見て肌で感じているだろうけれど、「あるサイトで多くのウェブページを保守する方法」については、裏方のスクリプトがクライアントサイドから見えないということもあって、まだ完全には答が出ていない――だれもが試行錯誤の状態かもしれない。

付記

「HTML論」には、往々、個人の習慣、美学、信仰、そしてコンプレックス(優越感と劣等感)が入ってくるようだが、ネットと言ってもHTTPまわりでしか生きていない者にとって、ウェブページは「自分の実存」だから、こだわりがあるのかもしれない。ほかのポート経由でも「存在」するようになると、また発想が相対的になるであろう。モデルさんは自分の外見を気にするが、同時に歌手でもあれば一日中、外見を気にしては、いないだろう。

このメモは、思いついたことを手早く書きとめたにすぎず、ほかの考え方や信心を否定する目的では、ない。いくつもの構造化のレベルがあるなかでどのレベルが最適の見方かということは、問題の性質によって異なる。どれかひとつが排他的に正しいわけでなく、場合によっては、スタイルシートなど使わない、しかもIEでしか表示できないようなページを作るのが、最適解になりうる。例えば、中古のマシンにIE3しか入ってないような途上国の小さな会社のLAN用にウェブで社内報を流すなら、そうなるかもしれない。