スタイルシートでは表現できない見栄えをJavaScriptで実現する方法の考察

uupaa.jsで実装中の機能について、思考(試行?)を記録する試み。
(この日記は、半年〜1年後に読み返すと意味があるかもしれない日記です。あえて さらしておきます)

考えてることとか・やりたいこと、

  1. JavaScript ONの環境で要素の見栄えを変更する良い方法はないものか。classNameの追加で見栄えを変化させる方法はそもそも妥当なのか? もっと良い方法は無いのか?
  2. classNameの変化で見栄えを変化させるとして、ユーザがuupaa.jsの機能を使用せずに、直接classNameを修正した場合はどうなるのか? 追従させる方法は?
  3. モジュール(機能)をプラガブルに追加できるuupaaでは、今は処理方法が不明でも後から処理できるモジュールがロードされることもある。そのようなケースはどうするか。
  4. 起動時に自動処理させる方法はどうすべきか。
  5. 起動後に、要素に変化かあった場合に見栄えを追従させる方法は?

まぁ、ぼちぼち。

課題1「className追加時に、JavaScriptで見栄えを変化させる方法」

たとえば「画像を10°傾けるには、classNameに "rotate" と "angle" + n を追加するを追加する。さらに rotate="angle10" を要素に追加する」と機能を定義したとして、

<img id="target" src="sample.jpg" alt="" />
<script>
uu.id("target").rotate = "angle10";
uu.id("target").className += " rotate";
</script>

classNameに追加された "rotate" を認識し、画像を10°傾けて表示する機能をいつだれが呼び出すのか?


classNameが追加された(変化した)ことを認識するには、

  1. 全ての要素のclassNameの値を保持しておく - アホすぎるので却下
  2. 変化時のイベントをキャッチする - IE以外ならセッターを追加するか、IEならonproprtychangeイベントハンドラで可能っちゃ可能だけど、上の方法と大差ないので却下
  3. classNameのアクセッサを通じて操作する(そして変化を認識する) - これが現実的

と、いくつか手段がある。

課題2 「window.load時に、JavaScriptで見栄えを変化させる方法」

window.load時にJavaScriptで見栄えを変化させるためには、

  • 全要素のclassNameを走査し、適切な関数を呼び出す

が有効な手段となりうるが、
classNameに含まれる文字列のパターンと、関数の紐付けが起動時に終了している必要があるため、

  • 一定間隔でclassNameを走査し、適切な関数を呼び出す

といったフォローが必要になるだろう。
紐付いていないものを処理済みとすると、モジュールをオンデマンドロード(遅延ロード)している場合に、ロード順や回線速度といった環境に依存した画面描画が起きてしまうため紐付いていないクラスを見つけたら、関数呼び出しをスキップする仕組みも必要になるだろう。

課題3 「一度処理済みの要素を再処理しない仕組み」

「起動時に見栄えを処理し、その後も一定間隔で処理可能な要素を探索する方法」が確立できても、まだ「処理済みの要素を再処理しない仕組み」が課題として残っている。

処理済みの要素に独自のマーキングを追加する方法

element.uuEffected = true

などがあるが、この方式の問題はメモリを浪費すること。

ならば、課題1の定義(「画像を10°傾けるには、classNameに "rotate" を追加する。さらに rotate="angle10" を要素に追加する」)を、
「画像を10°傾けるには、classNameに "jsrotate" を追加する。さらに rotate="angle10" を要素に追加する」とかに変えちゃって、

<img id="target" src="sample.jpg" alt="" />
<script>
uu.klass.add = function(elm, className) {
 if (classNameが"js"で始まっている) {
   classNameに紐付いている関数を呼び出す(コールバック)
   classNameの先頭のjsを取り除く
   elm.className += className;
 } else {
   elm.className += className;
 }
};

uu.id("target").rotate = "angle10";
uu.klass.add(uu.id("target"), "jsrotate");
</script>

のように、
uu.klass.add内で、"jsrotate " を "rotate" に差し替えてからtarget.classNameに追加し、
jsrotateに紐付いている関数を呼ぶ方式とすれば、

  • 起動時は、"js*"で始まるclassNameを持っている要素をピックアップし、"js"プリフィクスを除去し、classNameに関連付けられている関数を呼び出す。
  • その後は一定間隔で "js*"の要素を走査し、起動時と同様に処理する。
  • uu.klass.add経由で "js*"で始まるクラスを追加された場合も同様に処理する。
  • uu.klass.addを経由せずにclassNameが追加された場合(cloneNode, appendChild等も含む)は、一定間隔の走査により、処理することができる。
  • "js*"は、処理後に、"js"プリフィクスが除去されるため、再処理を心配しなくてよい。
  • uu.klass.addと一定間隔の走査は互いに排他を取る。

関数の呼び出しは、uu.msg.post()でメッセージをブロードキャストする方法でも代用可能かもしれない。

課題4 「JavaScriptがOFFの環境での挙動」

uupaa.jsJavaScriptライブラリなので、JavaScript OFF時のフォローは無視できる。

スクリプト経由ではなく、HTMLのスタイル属性でjsrotateや、独自のrotate属性が指定されていたとしても、

<style>
.rotate { ... }
</style>
<img id="target" src="sample.jpg" alt="" class="jsrotate" rotate="angle10" />

HTMLバリデータに苦言を言われるが副作用は無い(実際には無視される)。

さて、

CSSの能力を大きく超越した見栄えをJavaScriptで実現する」といった考え方は、
CSSの考え方(標準化)とは別のベクトルにあります。
それは、標準化の大切さをあえて無視した獣道です。Google先生の知らない前人未踏の道かもしれません。

でも、前を歩く人がいないと道はできないはずです。
引き返すのは今なのかなぁ? とビビりつつ。がんばって前に進んでみます。