JQuery中施用DE的绝对像素值hack的一处疑问
JQuery中使用DE的绝对像素值hack的一处疑问

可以看到,这里是为了规避opera抛出异常,所以加了一句判断。
本篇主要记录一下昨天和同事对JQ里取绝对像素值时对runTimeStyle的一处细节的讨论,同时提出自己的一些疑问。
疑问
最近我的同事 小卡 在整合jquery代码的时候,对JQuery里IE下取精确像素值的部分提出了一些疑问
下面的代码片段来自jquery-1.8.2.js:
currentStyle = function( elem, name ) { var left, rsLeft, uncomputed, ret = elem.currentStyle && elem.currentStyle[ name ], style = elem.style; // Avoid setting ret to empty string here // so we don't default to auto if ( ret == null && style && (uncomputed = style[ name ]) ) { ret = uncomputed; } // From the awesome hack by Dean Edwards // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 // If we're not dealing with a regular pixel number // but a number that has a weird ending, we need to convert it to pixels if ( rnumnonpx.test( ret ) ) { // Remember the original values //① left = style.left; rsLeft = elem.runtimeStyle && elem.runtimeStyle.left; // Put in the new values to get a computed value out //② if ( rsLeft ) { elem.runtimeStyle.left = elem.currentStyle.left; } //③ style.left = name === "fontSize" ? "1em" : ret; ret = style.pixelLeft + "px"; // Revert the changed values //④ style.left = left; if ( rsLeft ) { elem.runtimeStyle.left = rsLeft; } } return ret === "" ? "auto" : ret; };
主要是代码段②这里
// Put in the new values to get a computed value out if ( rsLeft ) { elem.runtimeStyle.left = elem.currentStyle.left; //疑问! }
疑问:这一句到底是干啥用的?
小卡试过 将这段去掉,在IE6/7/8/9里均不影响最终结果,即使runtimeStyle已经被人为设置过
解释?
我以前看JQ的时候也对DE的这段代码有过疑问,所以我按照那时候的理解给出的解释:
runtimeStyle是运行时样式,优先级最高
- 当runtimeStyle有值的时候,元素实际渲染总是以此为准,此时:
- 若用js取currentStyle的值时,引擎会返回runtimeStyle的值
- 若去设置style,元素的实际渲染不会发生变化的,因为style优先级很低,已经不作数了
- 当runtimeStyle的值不存在或者被清空(赋值''就是清空)时,元素的渲染以currentStyle为准。此时:
- 用js取currentStyle的值,就按照currentStyle自己的规则得到值。(currentStyle取值按照import-style,inline-style,HTML Attribute,HTML default根据样式优先级决定)
- 若去设置style,则有可能改变元素的渲染
所以结合JQ的代码来看:
①是备份style和runtimeStyle(因为后面需要还原)
②是一个必要的副作用消除步骤,因为接下来要通过设置style来取像素值,这里给最高优先级的runtimeStyle设置currentStyle,就能保证下面的过程不改变渲染
③是通过设置style.left然后再通过pixelLeft取像素值的过程
④是还原现场的过程
但 herbert 提出了质疑:
写道
如果runtimeStyle存在的话,currentStyle的值一定是runtimeStyle的值,这里赋值的意义何在?
我回过头看了一下上面的代码,发现还真是这么回事。
if ( rsLeft ) { //进了这个if,说明runtimeStyle.left有值 //当runtimeStyle.left有值时, currentStyle.left的值取出来就是runtimeStyle.left的值 //那下面这句赋值确实无意义啊!囧囧囧~~ elem.runtimeStyle.left = elem.currentStyle.left; }
为啥我以前研究这里的时候,没发现这个悖论呢?
溯源-反省
去找了下源头:
发现DE原始的代码里并无此判断
然后又翻了一下1.4的JQ,发现里面也是没有这句判断。
发现是1.5版本才增加的这个判断。
那么到底JQ里为啥要增加这句判断呢?
去Github看下history,发现2011年1月5号之前,源码里也是没有这个判断的
直到rwldrn一个针对opera的bugfix:
可以看到,这里是为了规避opera抛出异常,所以加了一句判断。
看到这里,只有两种可能性了:
A. 我之前的理解是错误的,这里并非为了担心下面对style.left赋值导致UI改变才写了这句话,而是有其他我暂未想到的原因
B. 我之前的理解是正确的,rwldrn的这个fix虽然规避了异常,但无意间使得对runtimeStyle的设置和恢复的代码,在常规情况下都不会进入了
那个可能性更大?
rwldrn提交了一个有副作用的fix(尽管可以看到,这个副作用不大),而这个错误在接下来的2年里,在无数人review的JQuery项目里,都未被发现?
所以我更倾向于我的理解是错误的。
到底咋解释?
再看一遍runTimeStyle的资料:
仍然没有思路
在网上找了一圈下来,发现大部分的理解和我基本相同
除了 franky 的这篇
...
其中要提的是 element.runtimeStyle 为什么这里也把他临时覆盖了呢? 可能是出于 保险。比如 其他代码中可能更改了此值 因为 runtimeStyle的优先级 要高越style... 所以为了pixelLeft取出正确的值 所以 也覆盖了element.runtimeStyle.
...
其中要提的是 element.runtimeStyle 为什么这里也把他临时覆盖了呢? 可能是出于 保险。比如 其他代码中可能更改了此值 因为 runtimeStyle的优先级 要高越style... 所以为了pixelLeft取出正确的值 所以 也覆盖了element.runtimeStyle.
...
franky的这个猜测,倒是和我的理解不同,但他的博文较早了,当时也仍然是基于没有那句if的讨论,所以当时看来也说得通
但现在jq里加上那句if后,这个解释仍然无法规避同样的悖论
实在想不出什么靠谱的解释了。。。
只能先上来po一篇博文,留待以后研究后补充结论。
也欢迎大神们来这里讨论~~
相关资料
汇总在这里(持续补充),供参考:
来源:
http://erik.eae.net/archives/2007/07/27/18.54.15/
两篇相关的中文blog:
http://yiminghe.iteye.com/blog/511589
http://www.cnblogs.com/_franky/archive/2009/11/29/1612969.html
runtimeStyle属性的介绍:
http://help.dottoro.com/ljhddfwr.php
http://erik.eae.net/archives/2007/07/27/18.54.15/
两篇相关的中文blog:
http://yiminghe.iteye.com/blog/511589
http://www.cnblogs.com/_franky/archive/2009/11/29/1612969.html
runtimeStyle属性的介绍:
http://help.dottoro.com/ljhddfwr.php
1 楼
司徒正美
昨天
这两个 if ( rsLeft ) { 直接去掉就行了
以前jQuery使用非A即B的方式,导致opera错误跑到currentStyle分支下
现在既然不会跑到这里,那就没用了
以前jQuery使用非A即B的方式,导致opera错误跑到currentStyle分支下
现在既然不会跑到这里,那就没用了
2 楼
hunter3721
15 小时前
I have already pulled a request here:
https://github.com/jquery/jquery/pull/1026
https://github.com/jquery/jquery/pull/1026