Hatena::ブログ(Diary)

latest log このページをアンテナに追加 RSSフィード

2009-12-12

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)
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)0x10x2
IE81.375ms 0.797ms 0.281ms
IE61.875ms 0.906ms 0.313ms
Google Chrome4(dev)測定不能 < 0ms
Firefox3.5.30.012ms
node.currentStyle との速度比較
IE8測定不能 < 0ms
IE60.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 または "" を指定してください。
  • 万能ではないので、ご利用の際は使いどころの見極めが必要です。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト

コメントを書くには、なぞなぞ認証に回答する必要があります。

トラックバック - http://d.hatena.ne.jp/uupaa/20091212/1260562931