Jintrick.netagenda2004年04月アーカイブ → 2004年04月07日

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

とことん標準に拘った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で遊ぶのが目的です。


webmaster@jintrick.net
公開: 2004年04月07日
カテゴリ: CSS ,DOM ,Javascript