agenda 2004-04(上旬)

とことん標準に拘ったCSS切り替えスクリプト(その5)

最終更新
2004-04-07T17:56:55+09:00

とことん標準に拘ったCSS切り替えスクリプト(その4)の続き。典拠の見直しとモデリング等々。

DOM Level 1 HTMLで一本化

2004年3月28日まで - 徒書にてご指摘頂きましたが、1.4. Association between a style sheet and a document(Document Object Model Style Sheets)で、link要素の生成によってスタイルシートが追加できる旨が記されています。ではDOM Level 2 Style Sheetsに対応していない場合には反映されないのかという疑問が出てきますが、DOMによる構造の変更が直ちに文書に反映される事を考えれば、明言されていなくてもこれは当然のことなのかもしれません。

問題は、DOM Level 2 Style Sheetsでは外部スタイルシートの追加に関する操作が全く定義されておらず、「追加はDOM HTMLでね」と言っている点にあります。DOM Level 2 Style Sheetsでやれることと言えば、少なくともCSS切り替えに関しては、スタイルシートを参照してdisabledプロパティを変更することだけです。それでさえDOM Level 1 HTMLで同等の操作が可能ですから、もはやDOM Level 2 Style Sheetsを使う意味はありません。単に対象ブラウザを少なくするだけです。よって、要求するDOMの実装はDOM Level 1 HTML(Core込み)のみにしたいと考えました。GUIの構築を援助するメソッドに関してはLevel 2 Eventsも要求します。

結果として application/xhtml+xml な文書は無視することになりますが、こちらの為の切り替えスクリプトは、効率の観点から、全く別のものとして考えるべきだと思います。

実装判別について

実装判別については、「標準に拘る」わけですから、そんなものは行わずに運用者に任せる形にします。この場合、未実装の場合にエラーを投げてやるだけです。tryで括って、必要なら勝手にcatchして勝手に非標準なコードを書いてくれという突き放し系。

グローバル変数等について

クライアントが既に使用しているスクリプト内のグローバル変数と名前がかぶっても変更の手間が少なくて済むよう、これは最小限に抑えます。考慮した結果、先に述べた「エラー」のコンストラクタ及び、「文書のCSS全体を表すオブジェクト」、この二つを提供することになりました。

function NotImplementedError(){}
function DocumentCSS(){}

また、関数オブジェクトそれぞれに、継承用のメソッドを追加します。具体的にはFunctionコンストラクタのprototypeにinheritメソッドを追加します。運用者のものとバッティングすると最悪ですが、より良いと確信できる方法が思い浮かばないので、取り敢えず先に進むことにします。

DocumentCSSオブジェクト

文書のCSS全体を表すオブジェクト、これを仮にDocumentCSSと名づけます。

大事なことですが、CSS切り替えが不可能なブラウザ(この場合DOM Level 1 HTML未実装ブラウザ)には迷惑がかからないようにしておくことが肝要です。よって、DocumentCSSは関数オブジェクトにし、呼び出さない限り中身が評価されないようにしておく必要があります。

DocumentCSSが呼び出された際、まず最初にDOMの実装チェックを行って、未実装ならばエラーを投げます。実装されていた場合、内部で利用するコンストラクタとエラーを定義し、自分自身を初期化(メソッドやプロパティを定義)し、最後に便宜上自分自身を返します。

function DocumentCSS(){

if (!document.implementation ||
  !document.implementation.hasFeature('HTML', '1.0'))
  throw new NotImplementedError('"DOM Level 1 HTML" is not implemented.');

function SomeConstructor(){}
function SomeError(){}

var self = arguments.callee;
self.someProperty = '';
self.someMethod = function(){};
return self;
}

DocumentCSSは文書のCSS全体を現すオブジェクトであり、呼び出すと自分自身を初期化します。関数オブジェクトというよりは、callableなオブジェクトと考えて設計しました。コンストラクタにしても良いのですが、インスタンスは必ず一つしかないので無意味です。

プロトタイプチェインを繋ぐ場合にはコンストラクタにしておいた方が便利ですが、例えばDocumentCSS.prototypeに各メソッドを追加するという作業は、DocumentCSSを利用できないブラウザにとっては迷惑でしかありません。

DocumentCSSで提供するもの

主にCSSを切り替える為のAPIを提供します。

DocumentCSS.change(styleName)

changeメソッドはスタイル名(link要素のtitle属性で表される)でCSSを切り替えます。空文字列を与えると、全てのCSSが無効化されます。title属性が空文字列になっているlink要素でリンクされたCSSは有効にすることができない、という弊害あり。

DocumentCSS.currentStyle

DocumentCSS.currentStyleプロパティは、現在適用されているCSSグループ(内部ではCSSGroupオブジェクトとして表現)のスタイル名です。全てのCSSが無効になっている場合は空文字列です。

DocumentCSS.styleSheets

DocumentCSS.styleSheetsプロパティは、全てのCSSGroupの辞書です。例えば、DocumentCSS.styleSheets['キャンパス']は、「キャンパス」という名前のCSSを全て含んだCSSGroupになります。そんなグループが無ければundefined

DocumentCSS.toSelectBox(DELETE_CK_FUNC)

DocumentCSS.toSelectBoxメソッドは、CSS切り替えの為のGUIを、select要素とbutton要素を含んだDocumentFragmentとして提供します。引数にCookieを削除する関数を与えると、Cookieを削除する為のbutton要素も生成します。

DOM Level 2 Events未実装の場合、NotImplementedErrorを投げます。使用する際にはtryブロックで括る必要があります。

DocumentCSS.createCSS(name, href, charset, media, isAlternate)

DocumentCSS.createCSSメソッドは、新たなCSSを作成して返却します。CSSとは具体的にはDocumentCSS内部で定義されたCSSStyleSheetオブジェクトです。これをcssとするなら、css.participant()で自動的に適切なCSSGroupに追加されます。固定CSSはname引数を空文字列にして作成します。hrefcharsetmedia は、HTMLのlink要素の属性に対応しています。isAlternateはオプションで、代替CSSか否かを真偽値で与えます。

Cookie

Cookieについては勝手にしろ、というところで落ち着きました。オマケのmain関数では一応用意する予定です。

Cookieの書き込みはDocumentCSS.currentStyleの値をsetCookie(key, value)系の関数に渡せば良いだけだし、getCookie(key)系の関数でCookieをとってきて、DocumentCSS.change(value);とすればCookieに基づいて切り替えられます。

使用例

典型的な使用例はこのような感じです。

window.onload = function() main{
	try
	{
		DocumentCSS(); // 初期化
	}
	catch(e)
	{
		if (e instanceof NotImplementedError)
			// DOM Level 1 HTMLが実装されていない。
			return;
		else
			// 一応デバッグ用に。
			throw e;
	}

	DocumentCSS.createCSS('弱視用', '../style/amblyopic.css', 'UTF-8').participant();

	try
	{
		var dfSel = DocumentCSS.toSelectBox(); // DocumentFragment
		document.body.appendChild(dfSel); // 適当
	}
	catch(e)
	{
		if (e instanceof NotImplementedError)
			// DOM Level 2 Event(HTMLEvents)が実装されていない。
			"pass"; // 代替となるGUIを提供する文書へのアンカーを作成するとか。
		else
			throw e;
	}
}

スクリプト本体

今のところ、まだ一応動くレベルです。というか、実用的なものならCSS切替スクリプト - 徒委記がお勧め。私のは脱毛しそうなストレスと戦いながらJavaScriptで遊ぶのが目的です。

リンクと参照

最終更新
2004-04-09T21:07:31+09:00

リンクは参照か - 徒書 及び iframeは参照か - 徒書 の補足を目的とした記事。別名「援護射撃」。

リンクとは何か

リンクとは、開始リソースと終了リソースとの間の関係、繋がりのことです。この関係もしくは繋がりをブラウザがどう表現するかは、リンクの本質ではありません(参照:link (Hypertext Terms))。

社会通念上は、クリック等すると画面遷移等が生じる「アンカーとなる文字列」のことをリンクと呼ぶようですが、ウェブページ制作者にとって、このような概念のごちゃまぜは恥です。

参照とは何か

参照とは、「あるリソースを他のリソースと照らし合わせてみること」です。また、「他のリソースを指し示すもの(ポインタ)」を意味することがあります。

  • あるリソースを他のリソースと照らし合わせてみること
  • 他のリソースを指し示すもの(ポインタ)

アンカーとなる文字列などはポインタとしての参照であると言えます。

ポインタというと、プログラム言語においては参照とポインタは微妙に異なる概念なので混乱するかもしれませんが、取り敢えず「他のリソースを指し示すもの(しかし実体ではないもの)」を便宜上ポインタと呼ぶことにします。

参照=ポインタというのは、ある方面の通念でしかないかも知れませんが、どちらにしろリンクとは違うことを示します。

リンクと参照の違い

ここに二つの文を比較してみます:

  • Googleをご覧ください
  • Googleをご覧ください

どちらも閲覧者に参照を促しており、Googleという文字列はどちらも閲覧者にとって他のリソースを指し示すポインタです。しかし後者は、Googleをa要素としてhref属性にURIを指定することによって、二つのリソース「文字列:Google」と「ウェブページ:http://www.google.com/」を関連付けています(リンクしています)。この関連がリンクです。リンク=参照、もしくはリンク=ポインタでないことは明らかです。

さらに、リンクした結果、閲覧者に対して参照やポインタが現れる必然性が無いことを、iframe要素の例で示します。

iframe要素は参照か

まずiframe要素とは何かということを考えます。iframe要素が作成するリンクにおいて、iframeという要素名が担う役割は、リンクの表現方法の指定です。そのsrc属性で示されたリソースを、その場所に埋め込むという表現方法の指定です。

iframe要素は、他のリソースを指し示す要素ではなく、それを文書内に埋め込む要素ですから、この要素はもはやポインタとしての参照でないことは明らかです。

ブラウザにとっては、src属性に示されたURIがポインタの役割を果たしますが、iframe要素をリンクの表現方法として選んだのは人間です。即ち、他のリソースを埋め込むという意思を持った人間です。埋め込むという意思を持っているのですから、これは参照行為でもありません。

以上より、iframe要素は、参照でもなければポインタでもありません。レンダリング過程において、そのsrc属性に示されたURIが、ブラウザ等のUser Agentにとってポインタであるだけです。

このiframeの例でも明らかになったように、リンクするということは、それが閲覧者に対する参照行為であるとも限らないし、また、閲覧者に用意されるポインタを作成するとも限りません。しかしブラウザ等のUser Agentに用意されるポインタは必ず存在します。

この「ブラウザにとってポインタである」という部分的な事実をもって、「iframe要素は参照である」と表現するのは、概念のごちゃまぜ、混乱の元、色々表現はありますが、兎も角正しくないと思います。