window.getComputedStyle for IE6+

IE6, IE7, IE8 用の window.getComputedStyle の実装(uu.cstyle.js)です。
uupaa.js 0.7 core の一部として開発していますが、これ自体はライブラリに依存していないため単体でも動きます。

uu.cstyle.js

/*!{id:"uu.cstyle.js",license:"MIT",author:"uupaa.js@gmail.com"}*/
window.getComputedStyle || (function() {
var _PT = /pt$/,
    _MORE,
    _UNIT = { m: 1, t: 2, "%": 3, o: 3 }, // em,pt,%,auto
    _THICK = (document.documentMode || 0) === 8 ? "5px" : "6px",
    _BOX_PROPS = [],
    _MOD_PROPS = { top: 1, left: 2, width: 3, height: 4 };

window.getComputedStyle = winstyle;

// window.getComputedStyle
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 {};
  }
  option = option || 0;
  var rv = {},
      ns = node.style,
      cs = node.currentStyle,
      rs = node.runtimeStyle,
      em, rect, unit, v, w, x, i = 0, m1, m2,
      stock = { "0px": "0px", "1px": "1px", "2px": "2px", "5px": "5px",
                thin: "1px", medium: "3px", thick: _THICK };

  if (!option) { // full
    for (w in cs) {
      rv[w] = cs[w];
    }
  } else if (option & 0x1) { // more
    while ( (w = _MORE[i++]) ) {
      rv[w] = cs[w] || ""; // IE8 propertyies -> IE6, IE7 down grade trap
    }
  }

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

  // calc border, padding and margin size
  i = 0;
  while ( (w = _BOX_PROPS[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_PROPS) {
    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_PROPS[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 box props
(function(ary, i, v) {
  while ( (v = ary[i++]) ) {
    _BOX_PROPS.push("border" + v + "Width", "margin" + v, "padding" + v);
  }
})("Top,Left,Right,Bottom".split(","), 0);

// option = 0x1, more properties (IE8 propertyies base)
_MORE = ( 
 "1Attachment,1Color,1Image,1PositionX,1PositionY,1Repeat,23Color,23Style,23W" +
 "idth,2LeftColor,2LeftStyle,2LeftWidth,2RightColor,2RightStyle,2RightWidth,2" +
 "TopColor,2TopStyle,2TopWidth,2Collapse,2Spacing,bottom,captionSide,clear,cl" +
 "ip3,clipLeft,clipRight,clipTop,color,cssFloat,cursor,direction,display,empt" +
 "yCells,fontFamily,fontSize,fontStyle,fontWeight,height,left,letterSpacing,l" +
 "ineBreak,lineHeight,listStyleImage,listStylePosition,listStyleType,margin3," +
 "marginLeft,marginRight,marginTop,maxHeight,maxWidth,minHeight,minWidth,outl" +
 "ineColor,outlineStyle,outlineWidth,overflow,overflowX,overflowY,padding3,pa" +
 "ddingLeft,paddingRight,paddingTop,position,right,styleFloat,textAlign,textA" +
 "utospace,textDecoration,textIndent,textJustify,textOverflow,textTransform,t" +
 "op,verticalAlign,visibility,whiteSpace,width,wordBreak,wordSpacing,wordWrap" +
 ",zIndex").replace(/1/g, "background").replace(/2/g, "border").
 replace(/3/g, "Bottom").split(",");
})();

HOW TO USE

<style>
#hoge {
  position: absolute; top: 20%; left: 5em;
}
</style>
<script src="uu.cstyle.js"></script>
<script>
window.onload = function() {
  var option = 0x0;
  var hash = window.getComputedStyle(document.getElementById("hoge"), null, option);
  alert(hash.top); // "80px"
}
</script>
<div id="hoge">hoge</div>
情報量(サブセット)の指定

第三引数で情報量(サブセット or フルセット)を指定できます。

  • window.getComputedStyle(node, null)
  • window.getComputedStyle(node, null, 0x0)
    • フルセットを返します
    • 一部のプロパティのみ px に変換しています(option = 0x2 で登場するプロパティのみ)
accelerator,
backgroundAttachment,
backgroundColor,
backgroundImage,
backgroundPositionX,
backgroundPositionY,
backgroundRepeat,
behavior,
blockDirection,
borderBottomColor,
borderBottomStyle,
borderBottomWidth,
borderCollapse,
borderColor,
borderLeftColor,
borderLeftStyle,
borderLeftWidth,
borderRightColor,
borderRightStyle,
borderRightWidth,
borderSpacing,
borderStyle,
borderTopColor,
borderTopStyle,
borderTopWidth,
borderWidth,
bottom,
boxSizing,
captionSide,
clear,
clipBottom,
clipLeft,
clipRight,
clipTop,
color,
cursor,
direction,
display,
emptyCells,
filter,
fontFamily,
fontSize,
fontStyle,
fontWeight,
hasLayout,
height,
imeMode,
layoutFlow,
layoutGridChar,
layoutGridLine,
layoutGridMode,
layoutGridType,
left,
letterSpacing,
lineBreak,
lineHeight,
listStyleImage,
listStylePosition,
listStyleType,
margin,
marginBottom,
marginLeft,
marginRight,
marginTop,
maxHeight,
maxWidth,
minHeight,
minWidth,
msBlockProgression,
msInterpolationMode,
orphans,
outline,
outlineColor,
outlineStyle,
outlineWidth,
overflow,
overflowX,
overflowY,
padding,
paddingBottom,
paddingLeft,
paddingRight,
paddingTop,
pageBreakAfter,
pageBreakBefore,
pageBreakInside,
position,
quotes,
right,
rubyAlign,
rubyOverhang,
rubyPosition,
scrollbar3dLightColor,
scrollbarArrowColor,
scrollbarBaseColor,
scrollbarDarkShadowColor,
scrollbarFaceColor,
scrollbarHighlightColor,
scrollbarShadowColor,
scrollbarTrackColor,
styleFloat,
tableLayout,
textAlign,
textAlignLast,
textAutospace,
textDecoration,
textIndent,
textJustify,
textJustifyTrim,
textKashida,
textKashidaSpace,
textOverflow,
textTransform,
textUnderlinePosition,
top,
unicodeBidi,
verticalAlign,
visibility,
whiteSpace,
widows,
width,
wordBreak,
wordSpacing,
wordWrap,
writingMode,
zIndex,
zoom,
  • window.getComputedStyle(node, null, 0x1)
    • 速度 + 使い勝手のバランスを考慮したサブセットを返します。
    • 一部のプロパティのみ px に変換しています(option = 0x2 で登場するプロパティのみ)
      • bottom や right は node.currentStyle が返す値そのままです。px に変換はしていません。
    • IE 独自のプロパティ(scroll〜) や 印刷系、ほぼ利用されないプロパティなどを含んでいません。
backgroundAttachment : scroll 
backgroundColor : transparent 
backgroundImage : none 
backgroundPositionX : 0% 
backgroundPositionY : 0% 
backgroundRepeat : repeat 
borderBottomColor : #000000 
borderBottomStyle : none 
borderBottomWidth : 3px 
borderCollapse : separate 
borderLeftColor : #000000 
borderLeftStyle : none 
borderLeftWidth : 3px 
borderRightColor : #000000 
borderRightStyle : none 
borderRightWidth : 3px 
borderSpacing : 
borderTopColor : #000000 
borderTopStyle : none 
borderTopWidth : 3px 
bottom : auto 
captionSide : top 
clear : none 
clipBottom : auto 
clipLeft : auto 
clipRight : auto 
clipTop : auto 
color : #000000 
cssFloat : none 
cursor : auto 
direction : ltr 
display : block 
emptyCells : show 
fontFamily : Times New Roman 
fontSize : 16px 
fontStyle : normal 
fontWeight :400
height : 1589px 
left : 80px 
letterSpacing : normal 
lineBreak : normal 
lineHeight : normal 
listStyleImage : none 
listStylePosition : outside 
listStyleType : disc 
marginBottom : 0px 
marginLeft : 0px 
marginRight : 0px 
marginTop : 0px 
maxHeight : none 
maxWidth : none 
minHeight : auto 
minWidth : auto 
outlineColor : #000000 
outlineStyle : none 
outlineWidth : 0px 
overflow : visible 
overflowX : visible 
overflowY : visible 
paddingBottom : 0px 
paddingLeft : 0px 
paddingRight : 0px 
paddingTop : 0px 
position : absolute 
right : auto 
styleFloat : none 
textAlign : left 
textAutospace : none 
textDecoration : none 
textIndent : 0pt 
textJustify : auto 
textOverflow : clip 
textTransform : none 
top : 126px 
verticalAlign : auto 
visibility : inherit 
whiteSpace : normal 
width : 189px 
wordBreak : normal 
wordSpacing : normal 
wordWrap : 
zIndex : auto
  • window.getComputedStyle(node, null, 0x2)
    • 速度重視で、最小構成のサブセットを返します。
    • このサブセットでは、単位が px に統一されています。
borderBottomWidth : 16px 
borderLeftWidth : 16px 
borderRightWidth : 16px 
borderTopWidth : 16px 
cssFloat : none
fontSize : 16px 
height : 6400px 
left : 88px 
marginBottom : 5px 
marginLeft : 5px 
marginRight : 5px 
marginTop : 5px 
paddingBottom : 1px 
paddingLeft : 1px 
paddingRight : 1px 
paddingTop : 1px 
top : 131px 
width : 300px 

速度的なファクター

window.getComputedStyle(node, null, option) の速度比較
指定しない(0x0) 0x1 0x2
IE8 1.375ms 0.797ms 0.281ms
IE6 1.875ms 0.906ms 0.313ms
Google Chrome4(dev) 測定不能 < 0ms
Firefox3.5.3 0.012ms
node.currentStyle との速度比較
IE8 測定不能 < 0ms
IE6 0.015ms

さっぱりテストできていないので

  • 不屈の精神をお持ちでない方には、お勧めできません。
  • バグがありそうだけど、網羅的なテストケースは作れそうにないので、とりあえず公開しました。

テストコード

<!doctype html><html><head><meta charset="utf-8" />
<title>window.getComputedStyle for IE</title>
<style>
#out {
  border: 1em solid green;
  margin: 5px;
  padding: 1px;
}
#out2 {
  border: 1em solid green;
  margin: 5px;
  padding: 1px;
  position: absolute;
  top: 20%;
  left: 88px;
  width: 300px;
  height: 400em;
}
</style>
<script src="http://uupaa-js.googlecode.com/svn/trunk/0.7/uu.js"></script>
<script src="uu.cstyle.js"></script>
<script>
function perf(node, loop, option) {
  var rv, i = 0;

  for (; i < loop; ++i) {
    if (1) {
      rv = window.getComputedStyle(node, null, option);
    } else {
      rv = node.currentStyle;
    }
  }
  return rv;
}
function xboot() {
  var node = document.getElementById("out2");
  var loop = 1000;
  var option = 0x2;
  var begin = +new Date;
  var hash = perf(node, loop, option);
  var span = +new Date - begin;

  node.innerHTML = uu.fmt("%j", hash).replace(/,/g, "<br />").replace(/\"/g, " ");
  node.innerHTML += "<hr />Time: " + (span / loop) + "ms";
}
</script></head><body>
<div id="out">out</div>
<div id="out2">out2</div>
</body></html>

あ、言い忘れた

  • サポートしている単位系は、 px pt em % のみです。
    • 0.5em ⇒ 10px とかは動きますが、 1cm ⇒ NG です。
  • ノードに所属していない(node.parentNode が null)なノードのスタイルは取れません。これは IE の仕様です。
    • IE 以外のブラウザだと、宙ぶらりんなノードでも、getComputedStyle でスタイルが取れたりします。
  • 第二引数(pseudo)を無視しています。null または "" を指定してください。
  • 万能ではないので、ご利用の際は使いどころの見極めが必要です。