对google个性主页的拖拽效果的js的完整注释(转载)

作者:Tin    

原文地址:http://www.blogjava.net/iamtin/archive/2006/04/27/43668.html

代码下载:http://www.blogjava.net/Files/iamtin/google_drag.rar

  1 // 工具类,使用Util的命名空间,方便管理 
  2 var  Util  =   new  Object();
  3 // 获取http header里面的UserAgent,浏览器信息 
  4 Util.getUserAgent  =  navigator.userAgent;
  5 // 是否是Gecko核心的Browser,比如Mozila、Firefox 
  6 Util.isGecko  =  Util.getUserAgent.indexOf( " Gecko " )  !=   - 1 ;
  7 // 是否是Opera 
  8 Util.isOpera  =  Util.getUserAgent.indexOf( " Opera " )  !=   - 1 ;
  9 // 获取一个element的offset信息,其实就是相对于Body的padding以内的绝对坐标 
 10 // 后面一个参数如果是true则获取offsetLeft,false则是offsetTop 
 11 // 关于offset、style、client等坐标的定义参考dindin的这个帖子:http://www.jroller.com/page/dindin/?anchor=pro_javascript_12 
 12 Util.getOffset  =   function  (el, isLeft) {
 13      var  retValue  =   0 ;
 14      while  (el  !=   null ) {
 15         retValue  +=  el[ " offset "   +  (isLeft  ?   " Left "  :  " Top " )];
 16         el  =  el.offsetParent;
 17     }
 18      return  retValue;
 19 };
 20 // 将一个function(参数中的funcName是这个fuction的名字)绑定到一个element上,并且以这个element的上下文运行,其实是一种继承,这个可以google些文章看看 
 21 Util.bindFunction  =   function  (el, fucName) {
 22      return   function  () {
 23          return  el[fucName].apply(el, arguments);
 24     };
 25 };
 26 // 重新计算所有的可以拖拽的element的坐标,对同一个column下面的可拖拽图层重新计算它们的高度而得出新的坐标,防止遮叠 
 27 // 计算出来的坐标记录在pagePosLeft和pagePosTop两个属性里面 
 28 Util.re_calcOff  =   function  (el) {
 29      for  ( var  i  =   0 ; i  <  Util.dragArray.length; i ++ ) {
 30          var  ele  =  Util.dragArray[i];
 31         ele.elm.pagePosLeft  =  Util.getOffset(ele.elm,  true );
 32         ele.elm.pagePosTop  =  Util.getOffset(ele.elm,  false );
 33     }
 34      var  nextSib  =  el.elm.nextSibling;
 35      while  (nextSib) {
 36         nextSib.pagePosTop  -=  el.elm.offsetHeight;
 37         nextSib  =  nextSib.nextSibling;
 38     }
 39 };
 40 
 41 // 隐藏Google Ig中间那个table,也就是拖拽的容器,配合show一般就是刷新用,解决一些浏览器的怪癖 
 42 Util.hide  =   function  () {
 43     Util.rootElement.style.display  =   " none " ;
 44 };
 45 // 显示Google Ig中间那个table,解释同上 
 46 Util.show  =   function  () {
 47     Util.rootElement.style.display  =   "" ;
 48 };
 49 
 50 // 移动时显示的占位虚线框 
 51 ghostElement  =   null ;
 52 // 获取这个虚线框,通过dom动态生成 
 53 getGhostElement  =   function  () {
 54      if  ( ! ghostElement) {
 55         ghostElement  =  document.createElement( " DIV " );
 56         ghostElement.className  =   " modbox " ;
 57         ghostElement.backgroundColor  =   "" ;
 58         ghostElement.style.border  =   " 2px dashed #aaa " ;
 59         ghostElement.innerHTML  =   " &nbsp; " ;
 60     }
 61      return  ghostElement;
 62 };
 63 
 64 // 初始化可以拖拽的Element的函数,与拖拽无关的我去掉了 
 65 function  draggable(el) {
 66      // 公用的开始拖拽的函数 
 67      this ._dragStart  =  start_Drag;
 68      // 公用的正在拖拽的函数 
 69      this ._drag  =  when_Drag;
 70      // 公用的拖拽结束的函数 
 71      this ._dragEnd  =  end_Drag;
 72      // 这个函数主要用来进行拖拽结束后的dom处理 
 73      this ._afterDrag  =  after_Drag;
 74      // 是否正在被拖动,一开始当然没有被拖动 
 75      this .isDragging  =   false ;
 76      // 将这个Element的this指针注册在elm这个变量里面,方便在自己的上下文以外调用自己的函数等,很常用的方法 
 77      this .elm  =  el;
 78      // 触发拖拽的Element,在这里就是这个div上显示标题的那个div 
 79      this .header  =  document.getElementById(el.id  +   " _h " );
 80      // 对于有iframe的element拖拽不同,这里检测一下并记录 
 81      this .hasIFrame  =   this .elm.getElementsByTagName( " IFRAME " ).length  >   0 ;
 82      // 如果找到了header就绑定drag相关的event 
 83      if  ( this .header) {
 84          // 拖拽时的叉子鼠标指针 
 85          this .header.style.cursor  =   " move " ;
 86          // 将函数绑定到header和element的this上,参照那个函数的说明 
 87         Drag.init( this .header,  this .elm);
 88          // 下面三个语句将写好的三个函数绑定给这个elemnt的三个函数钩子上,也就实现了element从draggable继承可拖拽的函数 
 89          this .elm.onDragStart  =  Util.bindFunction( this ,  " _dragStart " );
 90          this .elm.onDrag  =  Util.bindFunction( this ,  " _drag " );
 91          this .elm.onDragEnd  =  Util.bindFunction( this ,  " _dragEnd " );
 92     }
 93 };
 94 
 95 // 下面就是draggable里面用到的那4个function 
 96 // 公用的开始拖拽的函数 
 97 function  start_Drag() {
 98      // 重置坐标,实现拖拽以后自己的位置马上会被填充的效果 
 99     Util.re_calcOff( this );
100      // 记录原先的邻居节点,用来对比是否被移动到新的位置 
101      this .origNextSibling  =   this .elm.nextSibling;
102      // 获取移动的时候那个灰色的虚线框 
103      var  _ghostElement  =  getGhostElement();
104      // 获取正在移动的这个对象的高度 
105      var  offH  =   this .elm.offsetHeight;
106      if  (Util.isGecko) {
107          // 修正gecko引擎的怪癖吧 
108         offH  -=  parseInt(_ghostElement.style.borderTopWidth)  *   2 ;
109     }
110      // 获取正在移动的这个对象的宽度 
111      var  offW  =   this .elm.offsetWidth;
112      // 获取left和top的坐标 
113      var  offLeft  =  Util.getOffset( this .elm,  true );
114      var  offTop  =  Util.getOffset( this .elm,  false );
115      // 防止闪烁,现隐藏 
116     Util.hide();
117      // 将自己的宽度记录在style属性里面 
118      this .elm.style.width  =  offW  +   " px " ;
119      // 将那个灰框设定得与正在拖动的对象一样高,比较形象 
120     _ghostElement.style.height  =  offH  +   " px " ;
121      // 把灰框放到这个对象原先的位置上 
122      this .elm.parentNode.insertBefore(_ghostElement,  this .elm.nextSibling);
123      // 由于要拖动必须将被拖动的对象从原先的盒子模型里面抽出来,所以设定position为absolute,这个可以参考一下css布局方面的知识 
124      this .elm.style.position  =   " absolute " ;
125      // 设置zIndex,让它处在最前面一层,当然其实zIndex=100是让它很靠前,如果页面里有zIndex>100的,那…… 
126      this .elm.style.zIndex  =   100 ;
127      // 由于position=absolute了,所以left和top实现绝对坐标定位,这就是先前计算坐标的作用,不让这个模型乱跑,要从开始拖动的地方开始移动 
128      this .elm.style.left  =  offLeft  +   " px " ;
129      this .elm.style.top  =  offTop  +   " px " ;
130      // 坐标设定完毕,可以显示了,这样就不会闪烁了 
131     Util.show();
132      // 这里本来有个ig_d.G,没搞明白干什么用的,不过没有也可以用,谁知道麻烦告诉我一声,不好意思 
133      // 还没有开始拖拽,这里做个记号 
134      this .isDragging  =   false ;
135      return   false ;
136 };
137 // 在拖拽时的相应函数,由于绑定到鼠标的move这个event上,所以会传入鼠标的坐标clientX, clientY 
138 function  when_Drag(clientX, clientY) {
139      // 刚开始拖拽的时候将图层变透明,并标记为正在被拖拽 
140      if  ( ! this .isDragging) {
141          this .elm.style.filter  =   " alpha(opacity=70) " ;
142          this .elm.style.opacity  =   0.7 ;
143          this .isDragging  =   true ;
144     }
145      // 被拖拽到的新的column(当然也可以是原来那个) 
146      var  found  =   null ;
147      // 最大的距离,可能是防止溢出或者什么bug 
148      var  max_distance  =   100000000 ;
149      // 遍历所有的可拖拽的element,寻找离当前鼠标坐标最近的那个可拖拽元素,以便后面插入 
150      for  ( var  i  =   0 ; i  <  Util.dragArray.length; i ++ ) {
151          var  ele  =  Util.dragArray[i];
152          // 利用勾股定理计算鼠标到遍历到的这个元素的距离 
153          var  distance  =  Math.sqrt(Math.pow(clientX  -  ele.elm.pagePosLeft,  2 )  +  Math.pow(clientY  -  ele.elm.pagePosTop,  2 ));
154          // 自己已经浮动了,所以不计算自己的 
155          if  (ele  ==   this ) {
156              continue ;
157         }
158          // 如果计算失败继续循环 
159          if  (isNaN(distance)) {
160              continue ;
161         }
162          // 如果更小,记录下这个距离,并将它作为found 
163          if  (distance  <  max_distance) {
164             max_distance  =  distance;
165             found  =  ele;
166         }
167     }
168      // 准备让灰框落脚 
169      var  _ghostElement  =  getGhostElement();
170      // 如果找到了另外的落脚点 
171      if  (found  !=   null   &&  _ghostElement.nextSibling  !=  found.elm) {
172          // 找到落脚点就先把灰框插进去,这就是我们看到的那个灰框停靠的特效,有点像吸附的感觉,哈哈 
173         found.elm.parentNode.insertBefore(_ghostElement, found.elm);
174          if  (Util.isOpera) {
175              // Opera的现实问题,要隐藏/显示后才能刷新出变化 
176             document.body.style.display  =   " none " ;
177             document.body.style.display  =   "" ;
178         }
179     }
180 };
181 // 拖拽完毕 
182 function  end_Drag() {
183      // 拖拽完毕后执行后面的钩子,执行after_Drag(),如果布局发生了变动了就记录到远程服务器,保存你拖拽后新的布局顺序 
184      if  ( this ._afterDrag()) {
185          // remote call to save the change 
186     }
187      return   true ;
188 };
189 // 拖拽后的执行钩子 
190 function  after_Drag() {
191      var  returnValue  =   false ;
192      // 防止闪烁 
193     Util.hide();
194      // 把拖拽时的position=absolute和相关的那些style都消除 
195      this .elm.style.position  =   "" ;
196      this .elm.style.width  =   "" ;
197      this .elm.style.zIndex  =   "" ;
198      this .elm.style.filter  =   "" ;
199      this .elm.style.opacity  =   "" ;
200      // 获取灰框 
201      var  ele  =  getGhostElement();
202      // 如果现在的邻居不是原来的邻居了 
203      if  (ele.nextSibling  !=   this .origNextSibling) {
204          // 把被拖拽的这个节点插到灰框的前面 
205         ele.parentNode.insertBefore( this .elm, ele.nextSibling);
206          // 标明被拖拽了新的地方 
207         returnValue  =   true ;
208     }
209      // 移除灰框,这是这个灰框的生命周期应该就结束了 
210     ele.parentNode.removeChild(ele);
211      // 修改完毕,显示 
212     Util.show();
213      if  (Util.isOpera) {
214          // Opera的现实问题,要隐藏/显示后才能刷新出变化 
215         document.body.style.display  =   " none " ;
216         document.body.style.display  =   "" ;
217     }
218      return  returnValue;
219 };
220 // 可拖拽Element的原形,用来将event绑定到各个钩子,这部分市比较通用的,netvibes也是基本完全相同的实现 
221 // 这部分推荐看dindin的这个,也会帮助理解,http://www.jroller.com/page/dindin/?anchor=pro_javascript_12 
222 var  Drag  =  {
223      // 对这个element的引用,一次只能拖拽一个Element 
224     obj: null , 
225      // element是被拖拽的对象的引用,elementHeader就是鼠标可以拖拽的区域 
226     init: function  (elementHeader, element) {
227          // 将start绑定到onmousedown事件,按下鼠标触发start 
228         elementHeader.onmousedown  =  Drag.start;
229          // 将element存到header的obj里面,方便header拖拽的时候引用 
230         elementHeader.obj  =  element;
231          // 初始化绝对的坐标,因为不是position=absolute所以不会起什么作用,但是防止后面onDrag的时候parse出错了 
232          if  (isNaN(parseInt(element.style.left))) {
233             element.style.left  =   " 0px " ;
234         }
235          if  (isNaN(parseInt(element.style.top))) {
236             element.style.top  =   " 0px " ;
237         }
238          // 挂上空Function,初始化这几个成员,在Drag.init被调用后才帮定到实际的函数,可以参照draggable里面的内容 
239         element.onDragStart  =   new  Function();
240         element.onDragEnd  =   new  Function();
241         element.onDrag  =   new  Function();
242     },
243      // 开始拖拽的绑定,绑定到鼠标的移动的event上 
244     start: function  (event) {
245          var  element  =  Drag.obj  =   this .obj;
246          // 解决不同浏览器的event模型不同的问题 
247         event  =  Drag.fixE(event);
248          // 看看是不是左键点击 
249          if  (event.which  !=   1 ) {
250              // 除了左键都不起作用 
251              return   true ;
252         }
253          // 参照这个函数的解释,挂上开始拖拽的钩子 
254         element.onDragStart();
255          // 记录鼠标坐标 
256         element.lastMouseX  =  event.clientX;
257         element.lastMouseY  =  event.clientY;
258          // 将Global的event绑定到被拖动的element上面来 
259         document.onmouseup  =  Drag.end;
260         document.onmousemove  =  Drag.drag;
261          return   false ;
262     }, 
263      // Element正在被拖动的函数 
264     drag: function  (event) {
265          // 解决不同浏览器的event模型不同的问题 
266         event  =  Drag.fixE(event);
267          // 看看是不是左键点击 
268          if  (event.which  ==   0 ) {
269              // 除了左键都不起作用 
270              return  Drag.end();
271         }
272          // 正在被拖动的Element 
273          var  element  =  Drag.obj;
274          // 鼠标坐标 
275          var  _clientX  =  event.clientY;
276          var  _clientY  =  event.clientX;
277          // 如果鼠标没动就什么都不作 
278          if  (element.lastMouseX  ==  _clientY  &&  element.lastMouseY  ==  _clientX) {
279              return   false ;
280         }
281          // 刚才Element的坐标 
282          var  _lastX  =  parseInt(element.style.top);
283          var  _lastY  =  parseInt(element.style.left);
284          // 新的坐标 
285          var  newX, newY;
286          // 计算新的坐标:原先的坐标+鼠标移动的值差 
287         newX  =  _lastY  +  _clientY  -  element.lastMouseX;
288         newY  =  _lastX  +  _clientX  -  element.lastMouseY;
289          // 修改element的显示坐标 
290         element.style.left  =  newX  +   " px " ;
291         element.style.top  =  newY  +   " px " ;
292          // 记录element现在的坐标供下一次移动使用 
293         element.lastMouseX  =  _clientY;
294         element.lastMouseY  =  _clientX;
295          // 参照这个函数的解释,挂接上Drag时的钩子 
296         element.onDrag(newX, newY);
297          return   false ;
298     },
299      // Element正在被释放的函数,停止拖拽 
300     end: function  (event) {
301          // 解决不同浏览器的event模型不同的问题 
302         event  =  Drag.fixE(event);
303          // 解除对Global的event的绑定 
304         document.onmousemove  =   null ;
305         document.onmouseup  =   null ;
306          // 先记录下onDragEnd的钩子,好移除obj 
307          var  _onDragEndFuc  =  Drag.obj.onDragEnd();
308          // 拖拽完毕,obj清空 
309         Drag.obj  =   null ;
310          return  _onDragEndFuc;
311     }, 
312      // 解决不同浏览器的event模型不同的问题 
313     fixE: function  (ig_) {
314          if  ( typeof  ig_  ==   " undefined " ) {
315             ig_  =  window.event;
316         }
317          if  ( typeof  ig_.layerX  ==   " undefined " ) {
318             ig_.layerX  =  ig_.offsetX;
319         }
320          if  ( typeof  ig_.layerY  ==   " undefined " ) {
321             ig_.layerY  =  ig_.offsetY;
322         }
323          if  ( typeof  ig_.which  ==   " undefined " ) {
324             ig_.which  =  ig_.button;
325         }
326          return  ig_;
327     }
328 };
329 
330 
331 
332 // 下面是初始化的函数了,看看上面这些东西怎么被调用 
333 var  _IG_initDrag  =   function  (el) {
334      // column那个容器,在google里面就是那个table布局的tbody,netvibes用的<div> 
335     Util.rootElement  =  el;
336      // 这个tbody的行 
337     Util._rows  =  Util.rootElement.tBodies[ 0 ].rows[ 0 ];
338      // 列,google是3列,其实也可以更多 
339     Util.column  =  Util._rows.cells;
340      // 用来存取可拖拽的对象 
341     Util.dragArray  =   new  Array();
342      var  counter  =   0 ;
343      for  ( var  i  =   0 ; i  <  Util.column.length; i ++ ) {
344          // 搜索所有的column 
345          var  ele  =  Util.column[i];
346          for  ( var  j  =   0 ; j  <  ele.childNodes.length; j ++ ) {
347              // 搜索每一column里面的所有element 
348              var  ele1  =  ele.childNodes[j];
349              // 如果是div就把它初始化为一个draggable对象 
350              if  (ele1.tagName  ==   " DIV " ) {
351                 Util.dragArray[counter]  =   new  draggable(ele1);
352                 counter ++ ;
353             }
354         }
355     }
356 };
357 
358 // google的页面里可以拖动的部分的id是"t_1" 
359 // 挂载到onload,载入完毕执行。不过实际上google没有用onload。 
360 // 而是写在页面最下面,异曲同工吧,也许直接写在页面是种怪癖,或者也有可能是兼容性考虑。 
361 
362 // 请将下面两条被注释掉的代码加,到你自己下载的一个google ig页面里面,把里面的所有其余script删除,挂上这个js也可以拖拽了,哈哈 
363 // _table=document.getElementById("t_1"); 
364 // window.onload = _IG_initDrag(_table); 
365 
366 // 其实看懂这些代码对学习javascript很有益,希望对大家能有帮助

posted on 2016-06-14 11:43  我的前端之路  阅读(190)  评论(0)    收藏  举报

导航