window.getComputedStyle for IE6+ (その2)

ちょっと手直しして、

  • メインロジックはそのまま
  • コード量を 12% DOWN
    • minify + zip で 1.1kB
  • option = 0x0 の速度を 20% 〜 35% UP
IE8 1.375ms ⇒ 1.094ms
IE6 1.875ms ⇒ 1.218ms
  • option = 0x0 で列挙するプロパティを ホワイトリスト式 ⇒ ブラックリスト式 に変更
  • option = 0x0 で列挙するプロパティが A-Z 順でソート済の状態に
  • IE9 に window.getComputedStyle が実装されなかった場合を考慮
  • IE6, IE7 で動かした場合に、IE8 固有の CSS プロパティを列挙しないようした
    • IE の各バージョンで、列挙されてくるプロパティ数が変化するということ

といった版を作りました。

  • window.getComputedStyle(node, pseudo) では以下のプロパティを列挙しません。
    • accelerator, behavior, hasLayout, zoom
    • hasLayout 状態を調べるには node.currentStyle.hasLayout を直接参照してください
  • window.getComputedStyle(node, pseudo, 0x1) では以下のプロパティを列挙しません。
    • GALAPAGOS:
      • accelerator, behavior, hasLayout, zoom, filter, styleFloat, ms*, scrollbar*
    • SHORT HAND:
      • borderColor, borderStyle, borderWidth, margin, padding, outline
    • MINOR:
      • blockDirection, orphans, quotes, widows
      • imeMode, writingMode, unicodeBidi, emptyCells, tableLayout
      • layout*, ruby*, text* (textAlign textOverflow textIndent除く), pageBreak*
  • window.getComputedStyle(node, pseudo, 0x2) では以下のプロパティのみを列挙します。
    • borderBottomWidth, borderLeftWidth, borderRightWidth, borderTopWidth
    • marginBottom, marginLeft, marginRight, marginTop
    • paddingBottom, paddingLeft, paddingRight, paddingTop
    • top, left, width, height, fontSize, cssFloat

速度向上のためにやったこと

  • for in ループは遅いので while ( (v = ary[i++]) ) ループに変更
  • 列挙するプロパティの一覧を、初期化処理内で事前作成
window.getComputedStyle || (function() {
var _PT = /pt$/, _FULL = [], _MORE = [], _BOX = [],
    _MOD = { top: 1, left: 2, width: 3, height: 4 },
    _UNIT = { m: 1, t: 2, "%": 3, o: 3 }, // em, pt, %, auto
    _THICK = (document.documentMode || 0) > 7 ? "5px" : "6px";

window.getComputedStyle = winstyle;

function winstyle(node,     // @param Node:
                  pseudo,   // @param String(= void 0):
                  option) { // @param Number(= 0x0):
                            //   0x0: enum full properties
                            //   0x1: enum more properties
                            //   0x2: enum some properties
                            // @return Hash: { prop: "val", ... }
  if (!node.currentStyle) {
    return {};
  }
  var rv = {},
      ns = node.style,
      cs = node.currentStyle,
      rs = node.runtimeStyle,
      em, rect, unit, v, w, x, i = 0, j = 0, m1, m2,
      ary = !option ? _FULL : (option === 1) ? _MORE : 0,
      stock = { "0px": "0px", "1px": "1px", "2px": "2px", "5px": "5px",
                thin: "1px", medium: "3px", thick: _THICK };

  if (ary) {
    while ( (w = ary[j++]) ) {
      rv[w] = cs[w];
    }
  }

  em = parseFloat(cs.fontSize) * (_PT.test(cs.fontSize) ? 4 / 3 : 1);
  rect = node.getBoundingClientRect();

  // calc border, padding and margin size
  while ( (w = _BOX[i++]) ) {
    v = cs[w];
    if (!(v in stock)) {
      x = v;
      switch (unit = _UNIT[v.slice(-1)] || 0) {
      case 1: x = parseFloat(v) * em; break;    // em
      case 2: x = parseFloat(v) * 4 / 3; break; // pt
      case 3: m1 = ns.left, m2 = rs.left;       // %, auto
              rs.left = cs.left, ns.left = v;
              x = ns.pixelLeft, ns.left = m1, rs.left = m2;
      }
      stock[v] = unit ? x + "px" : x;
    }
    rv[w] = stock[v];
  }
  for (w in _MOD) {
    v = cs[w];
    switch (unit = _UNIT[v.slice(-1)] || 0) {
    case 1: v = parseFloat(v) * em; break;    // em
    case 2: v = parseFloat(v) * 4 / 3; break; // pt
    case 3: // %, auto
      switch (_MOD[w]) {
      case 1: v = node.offsetTop; break;
      case 2: v = node.offsetLeft; break;
      case 3: v = (node.offsetWidth  || rect.right - rect.left)
                - parseInt(rv.borderLeftWidth) - parseInt(rv.borderRightWidth)
                - parseInt(rv.paddingLeft) - parseInt(rv.paddingRight);
              v = v > 0 ? v : 0;
              break;
      case 4: v = (node.offsetHeight || rect.bottom - rect.top)
                - parseInt(rv.borderTopWidth) - parseInt(rv.borderBottomWidth)
                - parseInt(rv.paddingTop) - parseInt(rv.paddingBottom);
              v = v > 0 ? v : 0;
      }
    }
    rv[w] = unit ? v + "px" : v;
  }
  rv.fontSize = em + "px";
  rv.cssFloat = cs.styleFloat; // compat alias
  return rv;
}

// init - make _FULL, _MORE, _BOX props
(function() {
  var ary = [" "], i, w, trim = /^\s+|\s+$/g,
      cs = document.getElementsByTagName("html")[0].currentStyle;

  for (i in cs) {
    ary.push(i);
  }
  ary.sort();
  w = ary.join(" ").replace(/ (?:accelerator|behavior|hasLayout|zoom)/g, "");
  _FULL = w.replace(trim, "").split(" ");
  _MORE = w.replace(/ (?:lay\w+|rub\w+|text\w+|pageB\w+|ms\w+|scr\w+)/g, "").
    replace(/ (?:blockDirection|orphans|quotes|widows|filter|styleFloat)/g, "").
    replace(/ (?:imeMode|writingMode|unicodeBidi|emptyCells|tableLayout)/g, "").
    replace(/ (?:border(?:Color|Style|Width)|margin|padding|outline) /g, " ").
    replace(/ (border\w+Width|margin\w+|padding\w+)/g, function(_, m) {
      return _BOX.push(m), _;
    }).replace(trim, "").concat(" textAlign textOverflow textIndent").
    split(" ").sort();
})();

})();