本站首页    管理页面    写新日志    退出


«September 2025»
123456
78910111213
14151617181920
21222324252627
282930


公告
 本博客在此声明所有文章均为转摘,只做资料收集使用。

我的分类(专题)

日志更新

最新评论

留言板

链接

Blog信息
blog名称:
日志总数:1304
评论数量:2242
留言数量:5
访问次数:7613240
建立时间:2006年5月29日




[Prototype(Ajax)]prototype对于标签定位的一些BUG
软件技术

lhwork 发表于 2006/12/11 9:25:58

问题起因:在原来产品中实现的 ajax tree上面添加拖拽效果,为了方便,使用了prototype来简化开发。代码中使用了Poistion.absolutize来改变拖动标签时改变它 的坐标为绝对坐标显示,拖动结束后再使用Poistion.relativize变回相对坐标。解决过程:其实一开始测试时都挺 好的,但后来在tree上面使用时就发生问题了,在拖动过程,标签跟着鼠标的移动而改变,没有问题,但在鼠标释放后,标签并没有放置在鼠标释放的位置,而 是向左和向上偏移了,而这偏移的距离刚好就是tree显示位置的left和top。在对拖动结束后的位置计算的代码,拖动过程坐标计算的代码debug了 一天没有收获后,突然想到把样式中的滚动条设置(overflow-x : "auto", overflow-y: "scroll",)删掉试下,没想到就可以了。经过反复验证,终于证实是滚动条惹的祸,接着就跟踪了prototype中的相关代码,在实现Position.absolutize方法时是这样写的: 500)this.width=500'> 500)this.width=500'> Position.absolutize  =   function (element)  500)this.width=500'> {500)this.width=500'>    element  =  $(element);500)this.width=500'>     if  (element.style.position  ==  'absolute')  return ;500)this.width=500'>    Position.prepare();500)this.width=500'>500)this.width=500'> var  offsets  =  Position.positionedOffset(element);500)this.width=500'>500)this.width=500'>     var  top      =  offsets[ 1 ];500)this.width=500'>     var  left     =  offsets[ 0 ];500)this.width=500'>     var  width    =  element.clientWidth;500)this.width=500'>     var  height   =  element.clientHeight;500)this.width=500'>500)this.width=500'>    element._originalLeft    =  left  -  parseFloat(element.style.left   ||   0 );500)this.width=500'>    element._originalTop     =  top   -  parseFloat(element.style.top  ||   0 );500)this.width=500'>    element._originalWidth   =  element.style.width;500)this.width=500'>    element._originalHeight  =  element.style.height;500)this.width=500'>500)this.width=500'>    element.style.position  =  'absolute';500)this.width=500'>    element.style.top     =  top  +  'px';500)this.width=500'>    element.style.left    =  left  +  'px';500)this.width=500'>    element.style.width   =  width  +  'px';500)this.width=500'>    element.style.height  =  height  +  'px';500)this.width=500'>} ;500)this.width=500'> 其中Position.positionedOffset就是取当前标签到body的偏移量,然后将信息存入_original*的相关属性中,等到调用Position.relativize时,再从这些_original*属性中从新计算出当前标签的相对位置。再看一下Position.relativize的实现:500)this.width=500'>500)this.width=500'>500)this.width=500'>Position.relativize = function(element) 500)this.width=500'>{500)this.width=500'>    element = $(element);500)this.width=500'>500)this.width=500'>    if (element.style.position == 'relative') 500)this.width=500'>{500)this.width=500'>        return;    500)this.width=500'>    }500)this.width=500'>    500)this.width=500'>    Position.prepare();500)this.width=500'>    500)this.width=500'>    element.style.position = 'relative';500)this.width=500'>    var top  = parseFloat(element.style.top  || 0) - (element._originalTop  || 0);500)this.width=500'>    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);500)this.width=500'>    element.style.left   = left + 'px';500)this.width=500'>    element.style.top    = top  + 'px';500)this.width=500'>    element.style.height = element._originalHeight;500)this.width=500'>    element.style.width  = element._originalWidth;500)this.width=500'>}; 嗯,处理得非常漂亮,没有存在什么问题,以下是用来测试有html,试下会有什么效果500)this.width=500'><div style="height:50px"></div>500)this.width=500'><div style="width:500px;overflow-y:auto;height:300px">500)this.width=500'><div style="height:200px"></div>500)this.width=500'><div style="height:300px">500)this.width=500'><div id="test" style="height:20px">test</div>500)this.width=500'>    <input type="button" value="abs" onclick="Position.absolutize('test');">500)this.width=500'>    <input type="button" value="rel" onclick="Position.relativize('test');">500)this.width=500'>    </div>500)this.width=500'></div> 没 错,按下abs按钮后,test向下移了50px左右(第二个div的offsetTop),也向右移了一点(第二个div的offsetLeft)。 (如果把overflow-y:aut去掉,则没有此情况出现)而再按下rel按钮后,test能回复正常的位置,这就表示它在算法上没有什么问题,问题 出在了absolutize后的位置上了,而与位置相关的信息有 _originalTop 和_originalLeft,而它们的值是与Position.positionedOffset直接相关的,再查看了 Position.positionedOffset的代码:500)this.width=500'>500)this.width=500'>500)this.width=500'>Position.positionedOffset = function (element) 500)this.width=500'>{    500)this.width=500'>    var node = element.parentElement;    500)this.width=500'>    var valueT = 0, valueL = 0;500)this.width=500'>    500)this.width=500'>500)this.width=500'>    do 500)this.width=500'>{500)this.width=500'>        valueT += element.offsetTop || 0;500)this.width=500'>        valueL += element.offsetLeft || 0;500)this.width=500'>        element = element.offsetParent;500)this.width=500'>500)this.width=500'>        if (element) 500)this.width=500'>{        500)this.width=500'>            p = Element.getStyle(element, "position");500)this.width=500'>500)this.width=500'>            if (p == "relative" || p == "absolute" )500)this.width=500'>{500)this.width=500'>                break;500)this.width=500'>            }500)this.width=500'>        }500)this.width=500'>    } while (element);        500)this.width=500'>    500)this.width=500'>    return [valueL, valueT];500)this.width=500'>};看起来似乎也无法从中找出什么毛病来。可是,查了一下html的相关文档后,发现这段代码存在着相当严重的bug。html文档里,当样式position取绝对坐标"absolute "时,其内容如下:absolute :Object is positioned relative to parent element's position—or to the body object if its parent element is not positioned—using the top and left properties. 结合文档内容,经过测试,如果标签的所有祖先节点中,有任何一个是可滚动的(overflow,overflow-y,overflow-x其中一个属性的值为auto或scroll),那标签的绝对定位就是在此标签中的坐标位置,而不是对于BODY的。所以positionedOffset方法没有考虑到这种情况而处理,当然在一般情况下行得通了,所以代码更改如下:500)this.width=500'>500)this.width=500'>500)this.width=500'>Position.positionedOffset = function (element) 500)this.width=500'>{    500)this.width=500'>        500)this.width=500'>500)this.width=500'>    /**//*500)this.width=500'>     * 经过测试,如果标签的所有祖先节点中,有任何一个是可滚动的(overflow,overflow-y,overflow-x其中一个属性的值为auto或scroll),500)this.width=500'>     * 那标签的绝对定位就是在此标签中的坐标位置,而不是对于BODY的。所以在返回时应该将此祖先节点对于body的偏移量减掉.500)this.width=500'>     */500)this.width=500'>        500)this.width=500'>    var valueT = 0, valueL = 0;500)this.width=500'>    500)this.width=500'>500)this.width=500'>    do 500)this.width=500'>{500)this.width=500'>        valueT += element.offsetTop || 0;500)this.width=500'>        valueL += element.offsetLeft || 0;500)this.width=500'>        element = element.offsetParent;500)this.width=500'>500)this.width=500'>        if (element) 500)this.width=500'>{500)this.width=500'>            var scrollable = [element.style.overflow, element.style.overflowX, element.style.overflowY];500)this.width=500'>            p = Element.getStyle(element, "position");500)this.width=500'>500)this.width=500'>            if (p == "relative" || p == "absolute" || scrollable.include( "auto" ) || scrollable.include( "scroll" )) 500)this.width=500'>{500)this.width=500'>                break;500)this.width=500'>            }500)this.width=500'>        }500)this.width=500'>    } while (element);        500)this.width=500'>    500)this.width=500'>    return [valueL, valueT];500)this.width=500'>}; 至此,拖动后的标签定位问题终于解决,看来有时候人应该相信自己多一点,多怀疑一下别人的代码,正所谓,读书要善疑,更何况读别人的程序。


阅读全文(2732) | 回复(0) | 编辑 | 精华
 



发表评论:
昵称:
密码:
主页:
标题:
验证码:  (不区分大小写,请仔细填写,输错需重写评论内容!)



站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.063 second(s), page refreshed 144771264 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号