问题:
由于 css 属性单位可以设置为 em ,以及 % 来达到流体布局以及弹性布局的效果,但是当 javascript 操作这些元素进行运算时,一般要转换为绝对数值进行运算,比如 offsetWidth ,offsetHeight ,但是不是所有的css属性(padding,margin)都对应上述可以得到直接数值的属性,w3c 规范说明可以使用
document.defaultView.getComputedStyle(element,null).getPropertyValue(property);
来获得相应 css property 的像素绝对值(也不是完美的,存在缩写属性读取问题 ),而 ie 则完全没有办法 ,
element.currentStyle.property
只能获得用户指定的原始css 属性,比如 4em 或 20%,没有设置则为 auto
解决:
Dean Edwards 提出了针对 ie 的处理方式,利用了生僻的ie特有元素属性 runtimeStyle (运行时样式,比style优先级更高, 设置runtimestyle后再修改style对应属性,UI不会有对应变化) ,以及 style 某些属性相应的 pixel 属性,获取到了任意css 属性有效单元的运行时绝对像素数值。
From Secrets of the javacript ninja :
.runtimeStyle is analogous to .style with one exception: Any style properties set using
.runtimeStyle.prop will override .style.prop (which still leaving the value contained in
.style.prop intact).
.pixelLeft (and Top, Bottom, Right, Height, and Width) are properties for directly accessing the pixel
value of the respective CSS properties. (Note: These could be used as alternatives to even using Dean
Edwards' technique in the first place for these properties - Dean's technique is more useful for things like
font-size, line-height, etc.).
The technique works by setting the current computed left to the runtime left ('left' is chosen arbitrarily
- it could also work with top, right, etc.) - this will keep the positioning of the element intact while we
compute the pixel value. The pixel value is computed by setting the .style.left and then reading the
resulting pixel value out using .style.pixelLeft.
Dean's technique isn't perfect - especially when attempting to handle percentages on elements that are
a child of the body element (they end up expanding to consume a large portion of the document - 10%
becomes 200px, for example) but it ends up working well enough to handle most of the common use cases.
核心代码
var PIXEL = /^\d+(px)?$/i; function getPixelValue(element, value) { if (PIXEL.test(value)) return parseInt(value); var style = element.style.left; var runtimeStyle = element.runtimeStyle.left; element.runtimeStyle.left = element.currentStyle.left; element.style.left = value || 0; value = element.style.pixelLeft; element.style.left = style; element.runtimeStyle.left = runtimeStyle; return value; };
YUI3 解决:
dom-style-debug.js
相当于模拟实现了标准浏览器原生的GET_COMPUTED_STYLE:
if (!Y.config.win[GET_COMPUTED_STYLE]) { Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get; }
对于 width,height的数值获取采用 offsetWidth 减去 padding和边框的数值 ,而边框,padding,margin以及其他属性的数值则用上述的pixel方法获取。
IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset; IEComputed[BORDER_WIDTH] = IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] = IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] = ComputedStyle.getBorderWidth; IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom = IEComputed.marginLeft = ComputedStyle.getMargin;
注意: width,height 不能统一用 getPixelValue 来获取数值,width,height似乎有问题(为什么啊??pixelleft不能代表?),所以 yui3 区别判断用了offsetXX ,jquery也存在这个问题:(kissy 和 yui3 保持一致 )
<style>
#t {
width:80%;
position:relative;
left:80%;
}
</style>
<div id="t">
</div>
<script src="http://kissy.googlecode.com/svn/trunk/build/packages/ks-core.js"></script>
<script src="http://kissy.googlecode.com/svn/trunk/build/node/node-pkg-min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script>
<script type="text/javascript" charset="utf-8"
src="http://yui.yahooapis.com/3.1.1/build/yui/yui-min.js">
</script>
<script src="http://www.sencha.com/products/core/manual/js/ext-base.js"></script>
<script src="http://www.sencha.com/products/core/manual/js/extjs.js"></script>
<script>
YUI().use("node",function(Y){
var s=KISSY;
alert(s.one("#t").css("width"));
alert(s.one("#t")[0].offsetWidth);
alert(Y.one("#t").getStyle("width"));
alert(Ext.get("t").getStyle("width"));
alert("jquery : "+$("#t").css("width"));
});
</script>
ps:IE color问题
同样的,如果color设置为red,black等字符数值,则标准兼容浏览器可以使用getComputedStyle得到16进制数值,而ie则会取得字符串值,dean同样给出了ie专用的方法,利用IE专有的queryCommandValue ?
function toHex(color) { var body = createPopup().document.body, range = body.createTextRange(); body.style.color = color; var value = range.queryCommandValue("ForeColor"); value = ((value & 0x0000ff) << 16) | (value & 0x00ff00) | ((value & 0xff0000) >>> 16); value = value.toString(16); return "#000000".slice(0, 7 - value.length) + value; };
PS: IE 绝对定位 left(top) 问题
当元素为绝对定位且不设置left(top)时,ie 使用 currentStyle 取得为 auto,而我们如果使用上述的 pixelLeft可以得到为0,但是实际上由于left是和其相对定位的祖先有关,即使不设置也不为0的,在标准浏览器中可用 getComputedStyle 计算取得,或许有些库使用 offsetLeft 来代替 left ,实际上这是不对的,详见:Ext.Element.setXY 使用注意 bug in IE ? 。KISSY 中对这个问题特殊处理,使用 offsetLeft - margin-left :
// getComputedStyle for IE var RE_SPECIAL = /^auto$/i, RE_WH = /^width|height$/i, RE_LEFT = /^left$/i, POSITION = "position"; if (! (doc.defaultView || {}).getComputedStyle && docElem[CURRENT_STYLE]) { DOM._getComputedStyle = function (elem, name) { var style = elem.style, ret = elem[CURRENT_STYLE][name]; if (RE_SPECIAL.test(ret) && RE_LEFT.test(name)) { var position = DOM.css(elem, POSITION); //absolute: auto == offsetLeft - margin-left if (position == "absolute") ret = elem.offsetLeft - parseInt(DOM.css(elem, "margin-left")); //relative: auto ==0 else ret = 0; }.... } }