搜尋
首頁web前端js教程对google个性主页的拖拽效果的js的完整注释[转]_javascript技巧

作者: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           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           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  =  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用的
 
    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           // 搜索所有的column 
         var  ele  =  Util.column[i];
         for  ( var  j  =   0 ; 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很有益,希望对大家能有帮助 
陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

了解JavaScript引擎:實施詳細信息了解JavaScript引擎:實施詳細信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python vs. JavaScript:學習曲線和易用性Python vs. JavaScript:學習曲線和易用性Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

使用Next.js(後端集成)構建多租戶SaaS應用程序使用Next.js(後端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具