JavaScriptでCSSパーサーを書くための情報を収集中(5日目)

はい。5日目です。今日は「収集したルールを元に、要素にスタイルを適用する」です。

レガシーとかインラインとかは無かったことに

昨日は、レガシースタイルとインラインスタイルを収集して重み付けしましたが、今日はそのへんバッサリと削ってます。
# よくよく考えたら、いらない処理でした。

今日実装した部分

PHASE1〜2 を実装しました。PHASE3 は色々懸案事項があるので実装していません。

PHASE1. uuCSSParser.parse()
  STEP1. でスタイルを収集する
  STEP2. スタイルを重み付けでソートする(legacy, inlineは収集せず)

PHASE2. uuCSSParser.execute()
  STEP1. uuCSSParser 専用のスタイルシート(_sheet)を追加する
  STEP1. spec0 から 999 までの各ルールにIDを付ける
  STEP2. ".uucssp" + ID なルールを _sheet に追加
  STEP3. specが大きい順に CSSセレクタで要素を検索し、
         該当する要素の className の先頭に、"uucssp" + ID を追加
  STEP4. className を改変した目印として、element.uuCSSParser = "1" を設定

PHASE3. 未実装
  STEP1. spec1000以上の各ルールにIDを付ける
  STEP2. CSSセレクタで要素を検索し、
         要素の計算済みのスタイルと、!important で指定されたスタイルとの
         違いを検査。違いがあれば、element.style を直接改変する
  STEP3. element.style を改変した目印として、classNameに uuCSSParser = "2" を設定

http://uupaa-js-spinoff.googlecode.com/svn/trunk/uuCSSParser.js/uuCSSParser.js

(function() {
var _cssp, // inner namespace
    _mm = uuMeta,
    _win = window,
    _doc = document,
    _autoExec = !_win.UUCSSPARSER_DISABLE_AUTOEXEC, // 1 = disable auto execute
    _ie = _mm.ie,
    _sheet = 0, // private style sheet
    _urule = {}, // unique rule
    _uid = 0, // unique id
    /* 略 */

_cssp = {
  // uuCSSParser.parse - CSS Parser
  parse:
      function(css) { // @param CSSString(= ""):
                      // @return Hash: { specs: [spec-num1, spec-num2, ...],
                      //                 data[spec-num1]: { rule, expr, decl },
                      //                 data[spec-num2]: ... }
    var specs = [], data = {};

    _ie && _cssp._memento();
    if (!css) {
      css = _cssp._importStyleSheets();
    }
    _cssp._collectStyleSheets(specs, data, css);
    specs.sort(function(a, b) { return b - a; }); // sort of number(10 -> 1)
    return { specs: specs, data: data };
  },

  // uuCSSParser.execute - CSS Executor
  execute: function(hash) {
    _cssp._createStyleSheet();

    var v, w, i = 0, j, k, iz = hash.specs.length, jz, kz,
        spec, data, expr, id, node;

    for (; i < iz; ++i) {
      spec = hash.specs[i];
      data = hash.data[spec];

      if (spec >= 10000) { // !important
        // not impl.
      } else {
        for (j = 0, jz = data.length; j < jz; ++j) {
          expr = data[j].expr;
          id = _urule[expr] || (_urule[expr] = ++_uid);
          _cssp._addRule(".uucssp" + id, data[j].decl.join(";"));

          node = uuQuery(expr);
          for (k = 0, kz = node.length; k < kz; ++k) {
            v = node[k];
            w = v.className;
            v.className = "uucssp" + id + (w ? " " : "") + w;
            v.uuCSSParser = "1";
          }
        }
      }
    }
  },

  // uuCSSParser.recalc
  recalc: function() {
    _urule = {}, _uid = 0; // reset
    _sheet && _cssp._deleteRule(); // delete all

    var node = uuQuery("[uuCSSParser=1]"), v, i = 0, iz = node.length;

    for (; i < iz; ++i) {
      v = node[i];
      v.className = v.className.replace(/uucssp[\d]+\s*/g, "");
      v.uuCSSParser = "0";
    }
    _cssp.execute(_cssp.parse());
  },
  
  /*

   */
};

// --- initialize ---
function autoexec() {
  _cssp.execute(_cssp.parse());
  _mm.event.unbind(_win, "load", autoexec);
}
_autoExec && _mm.event.bind(_win, "load", autoexec);

_win.uuCSSParser = _cssp; // export

})(); // uuCSSParser scope

何ができるようになったか

古いブラウザで、最新のCSSが動作します。

<style>
div ul>li:nth-child(-n+3) { background-color: silver }
div ul>li:nth-child(even) { color: red }
</style>

デモ

IE6, IE7, Opera9.2x, Opera9.5x, Opera9.6x, Firefox2 なんかの古いブラウザで実行すると、わかりやすいと思います。


次回は…
うーん。どうしよう。