对google个性主页的拖拽效果的js的完整注释[转]


Posted in Javascript onApril 10, 2007

作者:Tin
出处:http://www.blogjava.net/iamtin/archive/2006/04/27/43668.html
代码:http://www.blogjava.net/Files/iamtin/google_drag.rar

// 工具类,使用Util的命名空间,方便管理  
var  Util  =   new  Object(); 
// 获取http header里面的UserAgent,浏览器信息  
Util.getUserAgent  =  navigator.userAgent; 
// 是否是Gecko核心的Browser,比如Mozila、Firefox  
Util.isGecko  =  Util.getUserAgent.indexOf( " Gecko " )  !=   - 1 ; 
// 是否是Opera  
Util.isOpera  =  Util.getUserAgent.indexOf( " Opera " )  !=   - 1 ; 
// 获取一个element的offset信息,其实就是相对于Body的padding以内的绝对坐标  
// 后面一个参数如果是true则获取offsetLeft,false则是offsetTop  
// 关于offset、style、client等坐标的定义参考dindin的这个帖子:http://www.jroller.com/page/dindin/?anchor=pro_javascript_12  
Util.getOffset  =   function  (el, isLeft) { 
     var  retValue  =   0 ; 
     while  (el  !=   null ) { 
        retValue  +=  el[ " offset "   +  (isLeft  ?   " Left "  :  " Top " )]; 
        el  =  el.offsetParent; 
    } 
     return  retValue; 
}; 
// 将一个function(参数中的funcName是这个fuction的名字)绑定到一个element上,并且以这个element的上下文运行,其实是一种继承,这个可以google些文章看看  
Util.bindFunction  =   function  (el, fucName) { 
     return   function  () { 
         return  el[fucName].apply(el, arguments); 
    }; 
}; 
// 重新计算所有的可以拖拽的element的坐标,对同一个column下面的可拖拽图层重新计算它们的高度而得出新的坐标,防止遮叠  
// 计算出来的坐标记录在pagePosLeft和pagePosTop两个属性里面  
Util.re_calcOff  =   function  (el) { 
     for  ( var  i  =   0 ; i  <  Util.dragArray.length; i ++ ) { 
         var  ele  =  Util.dragArray[i]; 
        ele.elm.pagePosLeft  =  Util.getOffset(ele.elm,  true ); 
        ele.elm.pagePosTop  =  Util.getOffset(ele.elm,  false ); 
    } 
     var  nextSib  =  el.elm.nextSibling; 
     while  (nextSib) { 
        nextSib.pagePosTop  -=  el.elm.offsetHeight; 
        nextSib  =  nextSib.nextSibling; 
    } 
}; // 隐藏Google Ig中间那个table,也就是拖拽的容器,配合show一般就是刷新用,解决一些浏览器的怪癖  
Util.hide  =   function  () { 
    Util.rootElement.style.display  =   " none " ; 
}; 
// 显示Google Ig中间那个table,解释同上  
Util.show  =   function  () { 
    Util.rootElement.style.display  =   "" ; 
}; 
// 移动时显示的占位虚线框  
ghostElement  =   null ; 
// 获取这个虚线框,通过dom动态生成  
getGhostElement  =   function  () { 
     if  ( ! ghostElement) { 
        ghostElement  =  document.createElement( " DIV " ); 
        ghostElement.className  =   " modbox " ; 
        ghostElement.backgroundColor  =   "" ; 
        ghostElement.style.border  =   " 2px dashed #aaa " ; 
        ghostElement.innerHTML  =   "   " ; 
    } 
     return  ghostElement; 
}; 
// 初始化可以拖拽的Element的函数,与拖拽无关的我去掉了  
function  draggable(el) { 
     // 公用的开始拖拽的函数  
     this ._dragStart  =  start_Drag; 
     // 公用的正在拖拽的函数  
     this ._drag  =  when_Drag; 
     // 公用的拖拽结束的函数  
     this ._dragEnd  =  end_Drag; 
     // 这个函数主要用来进行拖拽结束后的dom处理  
     this ._afterDrag  =  after_Drag; 
     // 是否正在被拖动,一开始当然没有被拖动  
     this .isDragging  =   false ; 
     // 将这个Element的this指针注册在elm这个变量里面,方便在自己的上下文以外调用自己的函数等,很常用的方法  
     this .elm  =  el; 
     // 触发拖拽的Element,在这里就是这个div上显示标题的那个div  
     this .header  =  document.getElementById(el.id  +   " _h " ); 
     // 对于有iframe的element拖拽不同,这里检测一下并记录  
     this .hasIFrame  =   this .elm.getElementsByTagName( " IFRAME " ).length  >   0 ; 
     // 如果找到了header就绑定drag相关的event  
     if  ( this .header) { 
         // 拖拽时的叉子鼠标指针  
         this .header.style.cursor  =   " move " ; 
         // 将函数绑定到header和element的this上,参照那个函数的说明  
        Drag.init( this .header,  this .elm); 
         // 下面三个语句将写好的三个函数绑定给这个elemnt的三个函数钩子上,也就实现了element从draggable继承可拖拽的函数  
         this .elm.onDragStart  =  Util.bindFunction( this ,  " _dragStart " ); 
         this .elm.onDrag  =  Util.bindFunction( this ,  " _drag " ); 
         this .elm.onDragEnd  =  Util.bindFunction( this ,  " _dragEnd " ); 
    } 
}; 
// 下面就是draggable里面用到的那4个function  
// 公用的开始拖拽的函数  
function  start_Drag() { 
     // 重置坐标,实现拖拽以后自己的位置马上会被填充的效果  
    Util.re_calcOff( this ); 
     // 记录原先的邻居节点,用来对比是否被移动到新的位置  
     this .origNextSibling  =   this .elm.nextSibling; 
     // 获取移动的时候那个灰色的虚线框  
     var  _ghostElement  =  getGhostElement(); 
     // 获取正在移动的这个对象的高度  
     var  offH  =   this .elm.offsetHeight; 
     if  (Util.isGecko) { 
         // 修正gecko引擎的怪癖吧  
        offH  -=  parseInt(_ghostElement.style.borderTopWidth)  *   2 ; 
    } 
     // 获取正在移动的这个对象的宽度  
     var  offW  =   this .elm.offsetWidth; 
     // 获取left和top的坐标  
     var  offLeft  =  Util.getOffset( this .elm,  true ); 
     var  offTop  =  Util.getOffset( this .elm,  false ); 
     // 防止闪烁,现隐藏  
    Util.hide(); 
     // 将自己的宽度记录在style属性里面  
     this .elm.style.width  =  offW  +   " px " ; 
     // 将那个灰框设定得与正在拖动的对象一样高,比较形象  
    _ghostElement.style.height  =  offH  +   " px " ; 
     // 把灰框放到这个对象原先的位置上  
     this .elm.parentNode.insertBefore(_ghostElement,  this .elm.nextSibling); 
     // 由于要拖动必须将被拖动的对象从原先的盒子模型里面抽出来,所以设定position为absolute,这个可以参考一下css布局方面的知识  
     this .elm.style.position  =   " absolute " ; 
     // 设置zIndex,让它处在最前面一层,当然其实zIndex=100是让它很靠前,如果页面里有zIndex>100的,那……  
     this .elm.style.zIndex  =   100 ; 
     // 由于position=absolute了,所以left和top实现绝对坐标定位,这就是先前计算坐标的作用,不让这个模型乱跑,要从开始拖动的地方开始移动  
     this .elm.style.left  =  offLeft  +   " px " ; 
     this .elm.style.top  =  offTop  +   " px " ; 
     // 坐标设定完毕,可以显示了,这样就不会闪烁了  
    Util.show(); 
     // 这里本来有个ig_d.G,没搞明白干什么用的,不过没有也可以用,谁知道麻烦告诉我一声,不好意思  
     // 还没有开始拖拽,这里做个记号  
     this .isDragging  =   false ; 
     return   false ; 
}; 
// 在拖拽时的相应函数,由于绑定到鼠标的move这个event上,所以会传入鼠标的坐标clientX, clientY  
function  when_Drag(clientX, clientY) { 
     // 刚开始拖拽的时候将图层变透明,并标记为正在被拖拽  
     if  ( ! this .isDragging) { 
         this .elm.style.filter  =   " alpha(opacity=70) " ; 
         this .elm.style.opacity  =   0.7 ; 
         this .isDragging  =   true ; 
    } 
     // 被拖拽到的新的column(当然也可以是原来那个)  
     var  found  =   null ; 
     // 最大的距离,可能是防止溢出或者什么bug  
     var  max_distance  =   100000000 ; 
     // 遍历所有的可拖拽的element,寻找离当前鼠标坐标最近的那个可拖拽元素,以便后面插入  
     for  ( var  i  =   0 ; i  <  Util.dragArray.length; i ++ ) { 
         var  ele  =  Util.dragArray[i]; 
         // 利用勾股定理计算鼠标到遍历到的这个元素的距离  
         var  distance  =  Math.sqrt(Math.pow(clientX  -  ele.elm.pagePosLeft,  2 )  +  Math.pow(clientY  -  ele.elm.pagePosTop,  2 )); 
         // 自己已经浮动了,所以不计算自己的  
         if  (ele  ==   this ) { 
             continue ; 
        } 
         // 如果计算失败继续循环  
         if  (isNaN(distance)) { 
             continue ; 
        } 
         // 如果更小,记录下这个距离,并将它作为found  
         if  (distance  <  max_distance) { 
            max_distance  =  distance; 
            found  =  ele; 
        } 
    } 
     // 准备让灰框落脚  
     var  _ghostElement  =  getGhostElement(); 
     // 如果找到了另外的落脚点  
     if  (found  !=   null   &&  _ghostElement.nextSibling  !=  found.elm) { 
         // 找到落脚点就先把灰框插进去,这就是我们看到的那个灰框停靠的特效,有点像吸附的感觉,哈哈  
        found.elm.parentNode.insertBefore(_ghostElement, found.elm); 
         if  (Util.isOpera) { 
             // Opera的现实问题,要隐藏/显示后才能刷新出变化  
            document.body.style.display  =   " none " ; 
            document.body.style.display  =   "" ; 
        } 
    } 
}; 
// 拖拽完毕  
function  end_Drag() { 
     // 拖拽完毕后执行后面的钩子,执行after_Drag(),如果布局发生了变动了就记录到远程服务器,保存你拖拽后新的布局顺序  
     if  ( this ._afterDrag()) { 
         // remote call to save the change  
    } 
     return   true ; 
}; 
// 拖拽后的执行钩子  
function  after_Drag() { 
     var  returnValue  =   false ; 
     // 防止闪烁  
    Util.hide(); 
     // 把拖拽时的position=absolute和相关的那些style都消除  
     this .elm.style.position  =   "" ; 
     this .elm.style.width  =   "" ; 
     this .elm.style.zIndex  =   "" ; 
     this .elm.style.filter  =   "" ; 
     this .elm.style.opacity  =   "" ; 
     // 获取灰框  
     var  ele  =  getGhostElement(); 
     // 如果现在的邻居不是原来的邻居了  
     if  (ele.nextSibling  !=   this .origNextSibling) { 
         // 把被拖拽的这个节点插到灰框的前面  
        ele.parentNode.insertBefore( this .elm, ele.nextSibling); 
         // 标明被拖拽了新的地方  
        returnValue  =   true ; 
    } 
     // 移除灰框,这是这个灰框的生命周期应该就结束了  
    ele.parentNode.removeChild(ele); 
     // 修改完毕,显示  
    Util.show(); 
     if  (Util.isOpera) { 
         // Opera的现实问题,要隐藏/显示后才能刷新出变化  
        document.body.style.display  =   " none " ; 
        document.body.style.display  =   "" ; 
    } 
     return  returnValue; 
}; 
// 可拖拽Element的原形,用来将event绑定到各个钩子,这部分市比较通用的,netvibes也是基本完全相同的实现  
// 这部分推荐看dindin的这个,也会帮助理解,http://www.jroller.com/page/dindin/?anchor=pro_javascript_12  
var  Drag  =  { 
     // 对这个element的引用,一次只能拖拽一个Element  
    obj: null ,  
     // element是被拖拽的对象的引用,elementHeader就是鼠标可以拖拽的区域  
    init: function  (elementHeader, element) { 
         // 将start绑定到onmousedown事件,按下鼠标触发start  
        elementHeader.onmousedown  =  Drag.start; 
         // 将element存到header的obj里面,方便header拖拽的时候引用  
        elementHeader.obj  =  element; 
         // 初始化绝对的坐标,因为不是position=absolute所以不会起什么作用,但是防止后面onDrag的时候parse出错了  
         if  (isNaN(parseInt(element.style.left))) { 
            element.style.left  =   " 0px " ; 
        } 
         if  (isNaN(parseInt(element.style.top))) { 
            element.style.top  =   " 0px " ; 
        } 
         // 挂上空Function,初始化这几个成员,在Drag.init被调用后才帮定到实际的函数,可以参照draggable里面的内容  
        element.onDragStart  =   new  Function(); 
        element.onDragEnd  =   new  Function(); 
        element.onDrag  =   new  Function(); 
    }, 
     // 开始拖拽的绑定,绑定到鼠标的移动的event上  
    start: function  (event) { 
         var  element  =  Drag.obj  =   this .obj; 
         // 解决不同浏览器的event模型不同的问题  
        event  =  Drag.fixE(event); 
         // 看看是不是左键点击  
         if  (event.which  !=   1 ) { 
             // 除了左键都不起作用  
             return   true ; 
        } 
         // 参照这个函数的解释,挂上开始拖拽的钩子  
        element.onDragStart(); 
         // 记录鼠标坐标  
        element.lastMouseX  =  event.clientX; 
        element.lastMouseY  =  event.clientY; 
         // 将Global的event绑定到被拖动的element上面来  
        document.onmouseup  =  Drag.end; 
        document.onmousemove  =  Drag.drag; 
         return   false ; 
    },  
     // Element正在被拖动的函数  
    drag: function  (event) { 
         // 解决不同浏览器的event模型不同的问题  
        event  =  Drag.fixE(event); 
         // 看看是不是左键点击  
         if  (event.which  ==   0 ) { 
             // 除了左键都不起作用  
             return  Drag.end(); 
        } 
         // 正在被拖动的Element  
         var  element  =  Drag.obj; 
         // 鼠标坐标  
         var  _clientX  =  event.clientY; 
         var  _clientY  =  event.clientX; 
         // 如果鼠标没动就什么都不作  
         if  (element.lastMouseX  ==  _clientY  &&  element.lastMouseY  ==  _clientX) { 
             return   false ; 
        } 
         // 刚才Element的坐标  
         var  _lastX  =  parseInt(element.style.top); 
         var  _lastY  =  parseInt(element.style.left); 
         // 新的坐标  
         var  newX, newY; 
         // 计算新的坐标:原先的坐标+鼠标移动的值差  
        newX  =  _lastY  +  _clientY  -  element.lastMouseX; 
        newY  =  _lastX  +  _clientX  -  element.lastMouseY; 
         // 修改element的显示坐标  
        element.style.left  =  newX  +   " px " ; 
        element.style.top  =  newY  +   " px " ; 
         // 记录element现在的坐标供下一次移动使用  
        element.lastMouseX  =  _clientY; 
        element.lastMouseY  =  _clientX; 
         // 参照这个函数的解释,挂接上Drag时的钩子  
        element.onDrag(newX, newY); 
         return   false ; 
    }, 
     // Element正在被释放的函数,停止拖拽  
    end: function  (event) { 
         // 解决不同浏览器的event模型不同的问题  
        event  =  Drag.fixE(event); 
         // 解除对Global的event的绑定  
        document.onmousemove  =   null ; 
        document.onmouseup  =   null ; 
         // 先记录下onDragEnd的钩子,好移除obj  
         var  _onDragEndFuc  =  Drag.obj.onDragEnd(); 
         // 拖拽完毕,obj清空  
        Drag.obj  =   null ; 
         return  _onDragEndFuc; 
    },  
     // 解决不同浏览器的event模型不同的问题  
    fixE: function  (ig_) { 
         if  ( typeof  ig_  ==   " undefined " ) { 
            ig_  =  window.event; 
        } 
         if  ( typeof  ig_.layerX  ==   " undefined " ) { 
            ig_.layerX  =  ig_.offsetX; 
        } 
         if  ( typeof  ig_.layerY  ==   " undefined " ) { 
            ig_.layerY  =  ig_.offsetY; 
        } 
         if  ( typeof  ig_.which  ==   " undefined " ) { 
            ig_.which  =  ig_.button; 
        } 
         return  ig_; 
    } 
}; 

// 下面是初始化的函数了,看看上面这些东西怎么被调用  
var  _IG_initDrag  =   function  (el) { 
     // column那个容器,在google里面就是那个table布局的tbody,netvibes用的<div>  
    Util.rootElement  =  el; 
     // 这个tbody的行  
    Util._rows  =  Util.rootElement.tBodies[ 0 ].rows[ 0 ]; 
     // 列,google是3列,其实也可以更多  
    Util.column  =  Util._rows.cells; 
     // 用来存取可拖拽的对象  
    Util.dragArray  =   new  Array(); 
     var  counter  =   0 ; 
     for  ( var  i  =   0 ; i  <  Util.column.length; i ++ ) { 
         // 搜索所有的column  
         var  ele  =  Util.column[i]; 
         for  ( var  j  =   0 ; j  <  ele.childNodes.length; j ++ ) { 
             // 搜索每一column里面的所有element  
             var  ele1  =  ele.childNodes[j]; 
             // 如果是div就把它初始化为一个draggable对象  
             if  (ele1.tagName  ==   " DIV " ) { 
                Util.dragArray[counter]  =   new  draggable(ele1); 
                counter ++ ; 
            } 
        } 
    } 
}; 
// google的页面里可以拖动的部分的id是"t_1"  
// 挂载到onload,载入完毕执行。不过实际上google没有用onload。  
// 而是写在页面最下面,异曲同工吧,也许直接写在页面是种怪癖,或者也有可能是兼容性考虑。  
// 请将下面两条被注释掉的代码加,到你自己下载的一个google ig页面里面,把里面的所有其余script删除,挂上这个js也可以拖拽了,哈哈  
// _table=document.getElementById("t_1");  
// window.onload = _IG_initDrag(_table);  
// 其实看懂这些代码对学习javascript很有益,希望对大家能有帮助 
Javascript 相关文章推荐
jQuery 1.4 15个你应该知道的新特性(译)
Jan 24 Javascript
js 处理URL实用技巧
Nov 23 Javascript
从js向Action传中文参数出现乱码问题的解决方法
Dec 29 Javascript
jQuery 获取/设置/删除DOM元素的属性以a元素为例
May 23 Javascript
js的延迟执行问题分析
Jun 23 Javascript
15个jquery常用方法、小技巧分享
Jan 13 Javascript
js 声明数组和向数组中添加对象变量的简单实例
Jul 28 Javascript
Vue-drag-resize 拖拽缩放插件的使用(简单示例)
Dec 04 Javascript
Javascript原型链及instanceof原理详解
May 25 Javascript
使用vue引入maptalks地图及聚合效果的实现
Aug 10 Javascript
OpenLayers3实现对地图的基本操作
Sep 28 Javascript
Nuxt.js 静态资源和打包的操作
Nov 06 Javascript
FireFox中textNode分片的问题
Apr 10 #Javascript
AJAX架构之Dojo篇
Apr 10 #Javascript
Dojo之路:如何利用Dojo实现Drag and Drop效果
Apr 10 #Javascript
给moz-firefox下添加IE方法和属性
Apr 10 #Javascript
用javascript父窗口控制只弹出一个子窗口
Apr 10 #Javascript
javascript 控制弹出窗口
Apr 10 #Javascript
javascript应用:Iframe自适应其加载的内容高度
Apr 10 #Javascript
You might like
PHP超牛逼无限极分类生成树方法
2015/05/11 PHP
PHP+Javascript实现在线拍照功能实例
2015/07/18 PHP
基于JQuery+PHP编写砸金蛋中奖程序
2015/09/08 PHP
jquery tab插件制作实现代码
2010/06/22 Javascript
使用jQuery全局事件ajaxStart为特定请求实现提示效果的代码
2010/12/30 Javascript
js给dropdownlist添加选项的小例子
2013/03/04 Javascript
jquery的flexigrid无法显示数据提示获取到数据
2013/07/19 Javascript
JS调用Android、Ios原生控件
2017/01/06 Javascript
Vue组件之Tooltip的示例代码
2017/10/18 Javascript
Vue父子模版传值及组件传值的三种方法
2017/11/27 Javascript
JS 中document.write()的用法和清空的原因浅析
2017/12/04 Javascript
微信小程序使用image组件显示图片的方法【附源码下载】
2017/12/08 Javascript
js 取消页面可以选中文字的功能方法
2018/01/02 Javascript
angular4强制刷新视图的方法
2018/10/09 Javascript
用js限制网页只在微信浏览器中打开(或者只能手机端访问)
2020/12/24 Javascript
解决vue中使用less/sass及使用中遇到无效的问题
2020/10/24 Javascript
[02:38]2018年度DOTA2最佳劣单位选手-完美盛典
2018/12/17 DOTA
Python socket.error: [Errno 98] Address already in use的原因和解决方法
2014/08/25 Python
python调用机器喇叭发出蜂鸣声(Beep)的方法
2015/03/23 Python
Python中Continue语句的用法的举例详解
2015/05/14 Python
Python request设置HTTPS代理代码解析
2018/02/12 Python
python读取word文档,插入mysql数据库的示例代码
2018/11/07 Python
pygame游戏之旅 添加icon和bgm音效的方法
2018/11/21 Python
使用Django连接Mysql数据库步骤
2019/01/15 Python
由Python编写的MySQL管理工具代码实例
2019/04/09 Python
Python学习之路之pycharm的第一个项目搭建过程
2020/06/18 Python
浅析Python 责任链设计模式
2020/09/11 Python
浅谈pc和移动端的响应式的使用
2019/01/03 HTML / CSS
基于Html5 canvas实现裁剪图片和马赛克功能及又拍云上传图片 功能
2019/07/09 HTML / CSS
德国2018年度最佳在线药房:Bodfeld Apotheke
2019/11/04 全球购物
工程部经理岗位职责
2013/12/08 职场文书
保安队长职务说明书
2014/02/23 职场文书
奥巴马就职演讲稿
2014/05/15 职场文书
公司贷款承诺书
2014/05/30 职场文书
呐喊读书笔记
2015/06/30 职场文书
《7的乘法口诀》教学反思
2016/02/18 职场文书