prototype对于标签定位的一些BUG [摘]

来源:互联网 发布:java怎样定义一个方法 编辑:程序博客网 时间:2024/05/16 00:52

http://www.blogjava.net/errorfun/archive/2006/12/09/86536.html

问题起因:
在原来产品中实现的 ajax tree上面添加拖拽效果,为了方便,使用了prototype来简化开发。代码中使用了Poistion.absolutize来改变拖动标签时改变它的坐标为绝对坐标显示,拖动结束后再使用Poistion.relativize变回相对坐标。

解决过程:
其实一开始测试时都挺好的,但后来在tree上面使用时就发生问题了,在拖动过程,标签跟着鼠标的移动而改变,没有问题,但在鼠标释放后,标签并没有放置在鼠标释放的位置,而是向左和向上偏移了,而这偏移的距离刚好就是tree显示位置的left和top。在对拖动结束后的位置计算的代码,拖动过程坐标计算的代码debug了一天没有收获后,突然想到把样式中的滚动条设置(overflow-x : "auto", overflow-y: "scroll",)删掉试下,没想到就可以了。

经过反复验证,终于证实是滚动条惹的祸,接着就跟踪了prototype中的相关代码,在实现Position.absolutize方法时是这样写的:

Position.absolutize  =   function (element)  {
    element 
=  $(element);
    
if  (element.style.position  ==  'absolute')  return ;
    Position.prepare();

var  offsets  =  Position.positionedOffset(element);

    
var  top      =  offsets[ 1 ];
    
var  left     =  offsets[ 0 ];
    
var  width    =  element.clientWidth;
    
var  height   =  element.clientHeight;

    element._originalLeft   
=  left  -  parseFloat(element.style.left   ||   0 );
    element._originalTop    
=  top   -  parseFloat(element.style.top  ||   0 );
    element._originalWidth  
=  element.style.width;
    element._originalHeight 
=  element.style.height;

    element.style.position 
=  'absolute';
    element.style.top    
=  top  +  'px';
    element.style.left   
=  left  +  'px';
    element.style.width  
=  width  +  'px';
    element.style.height 
=  height  +  'px';
}
;


其中Position.positionedOffset就是取当前标签到body的偏移量,然后将信息存入_original*的相关属性中,等到调用Position.relativize时,再从这些_original*属性中从新计算出当前标签的相对位置。
再看一下Position.relativize的实现:

嗯,处理得非常漂亮,没有存在什么问题,以下是用来测试有html,试下会有什么效果

没错,按下abs按钮后,test向下移了50px左右(第二个div的offsetTop),也向右移了一点(第二个div的offsetLeft)。(如果把overflow-y:aut去掉,则没有此情况出现)而再按下rel按钮后,test能回复正常的位置,这就表示它在算法上没有什么问题,问题出在了absolutize后的位置上了,而与位置相关的信息有 _originalTop 和_originalLeft,而它们的值是与Position.positionedOffset直接相关的,再查看了Position.positionedOffset的代码:

至此,拖动后的标签定位问题终于解决,看来有时候人应该相信自己多一点,多怀疑一下别人的代码,正所谓,读书要善疑,更何况读别人的程序。
 


Position.relativize 
= function(element) {
    element 
= $(element);
    
if (element.style.position == 'relative') {
        
return;    
    }

    
    Position.prepare();
    
    element.style.position 
= 'relative';
    
var top  = parseFloat(element.style.top  || 0- (element._originalTop  || 0);
    
var left = parseFloat(element.style.left || 0- (element._originalLeft || 0);
    element.style.left   
= left + 'px';
    element.style.top    
= top  + 'px';
    element.style.height 
= element._originalHeight;
    element.style.width  
= element._originalWidth;
}
;

 

<div style="height:50px"></div>
<div style="width:500px;overflow-y:auto;height:300px">
<div style="height:200px"></div>
<div style="height:300px">
<div id="test" style="height:20px">test</div>
    
<input type="button" value="abs" onclick="Position.absolutize('test');">
    
<input type="button" value="rel" onclick="Position.relativize('test');">
    
</div>
</div>

 


Position.positionedOffset 
= function (element) {    
    
var node = element.parentElement;    
    
var valueT = 0, valueL = 0;
    
    
do {
        valueT 
+= element.offsetTop || 0;
        valueL 
+= element.offsetLeft || 0;
        element 
= element.offsetParent;
        
if (element) {        
            p 
= Element.getStyle(element, "position");
            
if (p == "relative" || p == "absolute" ){
                
break;
            }

        }

    }
 while (element);        
    
    
return [valueL, valueT];
}
;

看起来似乎也无法从中找出什么毛病来。可是,查了一下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方法没有考虑到这种情况而处理,当然在一般情况下行得通了,所以代码更改如下:


Position.positionedOffset 
= function (element) {    
    
    
    
/*
     * 经过测试,如果标签的所有祖先节点中,有任何一个是可滚动的(overflow,overflow-y,overflow-x其中一个属性的值为auto或scroll),
     * 那标签的绝对定位就是在此标签中的坐标位置,而不是对于BODY的。所以在返回时应该将此祖先节点对于body的偏移量减掉.
     
*/

        
    
var valueT = 0, valueL = 0;
    
    
do {
        valueT 
+= element.offsetTop || 0;
        valueL 
+= element.offsetLeft || 0;
        element 
= element.offsetParent;
        
if (element) {
            
var scrollable = [element.style.overflow, element.style.overflowX, element.style.overflowY];
            p 
= Element.getStyle(element, "position");
            
if (p == "relative" || p == "absolute" || scrollable.include( "auto" ) || scrollable.include( "scroll" )) {
                
break;
            }

        }

    }
 while (element);        
    
    
return [valueL, valueT];
}
;

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 燃气灶开关松了怎么办 厨房插座挨灶台怎么办 天然气灶费电池怎么办 国美东西买贵啦怎么办 饥荒咕咕鸟死了怎么办 收到催天下信息怎么办 对门邻居有白事怎么办 顺丰理赔不合理怎么办 手机店抽奖被骗怎么办 手机店投票被骗怎么办 锤子手机进水了怎么办 锤子手机无法关机怎么办 坚果pro2卡顿怎么办 兴隆破产兴隆卡怎么办 电脑开机键失灵怎么办 网上买冰箱售后怎么办 物流公司损坏了怎么办 白色充电器线脏了怎么办 卷尺缩不回去怎么办 在昆山怎么办电瓶车牌 网购遇到质量问题怎么办 洗衣机外壳坏了怎么办 8元飞享套餐下线怎么办 改光纤后传真机怎么办 网上预约迟到了怎么办 国地税合并局长怎么办? 信用卡申请条件不足怎么办 深户落亲戚房产怎么办准迁证 从深圳迁出户口怎么办 快递未送达签收怎么办 床垫用水洗后怎么办 轿车空调不制冷怎么办 空调氟漏完了怎么办 一窗式比对异常怎么办 高铁票无座怎么办 公司把社保断交怎么办 电信宽带电视无信号怎么办 电信电视没信号怎么办 苹果键盘进水了怎么办 电信怎么办日流量包 济南华强坑人怎么办