// 工具類,使用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 ; // 獲取一個(gè)element的offset信息,其實(shí)就是相對(duì)于Body的padding以內(nèi)的絕對(duì)坐標(biāo) // 后面一個(gè)參數(shù)如果是true則獲取offsetLeft,false則是offsetTop // 關(guān)于offset、style、client等坐標(biāo)的定義參考dindin的這個(gè)帖子:http://www./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; }; // 將一個(gè)function(參數(shù)中的funcName是這個(gè)fuction的名字)綁定到一個(gè)element上,并且以這個(gè)element的上下文運(yùn)行,其實(shí)是一種繼承,這個(gè)可以google些文章看看 Util.bindFunction = function (el, fucName) { return function () { return el[fucName].apply(el, arguments); }; }; // 重新計(jì)算所有的可以拖拽的element的坐標(biāo),對(duì)同一個(gè)column下面的可拖拽圖層重新計(jì)算它們的高度而得出新的坐標(biāo),防止遮疊 // 計(jì)算出來(lái)的坐標(biāo)記錄在pagePosLeft和pagePosTop兩個(gè)屬性里面 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中間那個(gè)table,也就是拖拽的容器,配合show一般就是刷新用,解決一些瀏覽器的怪癖 Util.hide = function () { Util.rootElement.style.display = " none " ; }; // 顯示Google Ig中間那個(gè)table,解釋同上 Util.show = function () { Util.rootElement.style.display = "" ; }; // 移動(dòng)時(shí)顯示的占位虛線框 ghostElement = null ; // 獲取這個(gè)虛線框,通過(guò)dom動(dòng)態(tài)生成 getGhostElement = function () { if ( ! ghostElement) { ghostElement = document.createElement( " DIV " ); ghostElement.className = " modbox " ; ghostElement.backgroundColor = "" ; ghostElement.style.border = " 2px dashed #aaa " ; ghostElement.innerHTML = " " ; } return ghostElement; }; // 初始化可以拖拽的Element的函數(shù),與拖拽無(wú)關(guān)的我去掉了 function draggable(el) { // 公用的開(kāi)始拖拽的函數(shù) this ._dragStart = start_Drag; // 公用的正在拖拽的函數(shù) this ._drag = when_Drag; // 公用的拖拽結(jié)束的函數(shù) this ._dragEnd = end_Drag; // 這個(gè)函數(shù)主要用來(lái)進(jìn)行拖拽結(jié)束后的dom處理 this ._afterDrag = after_Drag; // 是否正在被拖動(dòng),一開(kāi)始當(dāng)然沒(méi)有被拖動(dòng) this .isDragging = false ; // 將這個(gè)Element的this指針注冊(cè)在elm這個(gè)變量里面,方便在自己的上下文以外調(diào)用自己的函數(shù)等,很常用的方法 this .elm = el; // 觸發(fā)拖拽的Element,在這里就是這個(gè)div上顯示標(biāo)題的那個(gè)div this .header = document.getElementById(el.id + " _h " ); // 對(duì)于有iframe的element拖拽不同,這里檢測(cè)一下并記錄 this .hasIFrame = this .elm.getElementsByTagName( " IFRAME " ).length > 0 ; // 如果找到了header就綁定drag相關(guān)的event if ( this .header) { // 拖拽時(shí)的叉子鼠標(biāo)指針 this .header.style.cursor = " move " ; // 將函數(shù)綁定到header和element的this上,參照那個(gè)函數(shù)的說(shuō)明 Drag.init( this .header, this .elm); // 下面三個(gè)語(yǔ)句將寫(xiě)好的三個(gè)函數(shù)綁定給這個(gè)elemnt的三個(gè)函數(shù)鉤子上,也就實(shí)現(xiàn)了element從draggable繼承可拖拽的函數(shù) this .elm.onDragStart = Util.bindFunction( this , " _dragStart " ); this .elm.onDrag = Util.bindFunction( this , " _drag " ); this .elm.onDragEnd = Util.bindFunction( this , " _dragEnd " ); } }; // 下面就是draggable里面用到的那4個(gè)function // 公用的開(kāi)始拖拽的函數(shù) function start_Drag() { // 重置坐標(biāo),實(shí)現(xiàn)拖拽以后自己的位置馬上會(huì)被填充的效果 Util.re_calcOff( this ); // 記錄原先的鄰居節(jié)點(diǎn),用來(lái)對(duì)比是否被移動(dòng)到新的位置 this .origNextSibling = this .elm.nextSibling; // 獲取移動(dòng)的時(shí)候那個(gè)灰色的虛線框 var _ghostElement = getGhostElement(); // 獲取正在移動(dòng)的這個(gè)對(duì)象的高度 var offH = this .elm.offsetHeight; if (Util.isGecko) { // 修正gecko引擎的怪癖吧 offH -= parseInt(_ghostElement.style.borderTopWidth) * 2 ; } // 獲取正在移動(dòng)的這個(gè)對(duì)象的寬度 var offW = this .elm.offsetWidth; // 獲取left和top的坐標(biāo) var offLeft = Util.getOffset( this .elm, true ); var offTop = Util.getOffset( this .elm, false ); // 防止閃爍,現(xiàn)隱藏 Util.hide(); // 將自己的寬度記錄在style屬性里面 this .elm.style.width = offW + " px " ; // 將那個(gè)灰框設(shè)定得與正在拖動(dòng)的對(duì)象一樣高,比較形象 _ghostElement.style.height = offH + " px " ; // 把灰框放到這個(gè)對(duì)象原先的位置上 this .elm.parentNode.insertBefore(_ghostElement, this .elm.nextSibling); // 由于要拖動(dòng)必須將被拖動(dòng)的對(duì)象從原先的盒子模型里面抽出來(lái),所以設(shè)定position為absolute,這個(gè)可以參考一下css布局方面的知識(shí) this .elm.style.position = " absolute " ; // 設(shè)置zIndex,讓它處在最前面一層,當(dāng)然其實(shí)zIndex=100是讓它很靠前,如果頁(yè)面里有zIndex>100的,那…… this .elm.style.zIndex = 100 ; // 由于position=absolute了,所以left和top實(shí)現(xiàn)絕對(duì)坐標(biāo)定位,這就是先前計(jì)算坐標(biāo)的作用,不讓這個(gè)模型亂跑,要從開(kāi)始拖動(dòng)的地方開(kāi)始移動(dòng) this .elm.style.left = offLeft + " px " ; this .elm.style.top = offTop + " px " ; // 坐標(biāo)設(shè)定完畢,可以顯示了,這樣就不會(huì)閃爍了 Util.show(); // 這里本來(lái)有個(gè)ig_d.G,沒(méi)搞明白干什么用的,不過(guò)沒(méi)有也可以用,誰(shuí)知道麻煩告訴我一聲,不好意思 // 還沒(méi)有開(kāi)始拖拽,這里做個(gè)記號(hào) this .isDragging = false ; return false ; }; // 在拖拽時(shí)的相應(yīng)函數(shù),由于綁定到鼠標(biāo)的move這個(gè)event上,所以會(huì)傳入鼠標(biāo)的坐標(biāo)clientX, clientY function when_Drag(clientX, clientY) { // 剛開(kāi)始拖拽的時(shí)候?qū)D層變透明,并標(biāo)記為正在被拖拽 if ( ! this .isDragging) { this .elm.style.filter = " alpha(opacity=70) " ; this .elm.style.opacity = 0.7 ; this .isDragging = true ; } // 被拖拽到的新的column(當(dāng)然也可以是原來(lái)那個(gè)) var found = null ; // 最大的距離,可能是防止溢出或者什么bug var max_distance = 100000000 ; // 遍歷所有的可拖拽的element,尋找離當(dāng)前鼠標(biāo)坐標(biāo)最近的那個(gè)可拖拽元素,以便后面插入 for ( var i = 0 ; i < Util.dragArray.length; i ++ ) { var ele = Util.dragArray[i]; // 利用勾股定理計(jì)算鼠標(biāo)到遍歷到的這個(gè)元素的距離 var distance = Math.sqrt(Math.pow(clientX - ele.elm.pagePosLeft, 2 ) + Math.pow(clientY - ele.elm.pagePosTop, 2 )); // 自己已經(jīng)浮動(dòng)了,所以不計(jì)算自己的 if (ele == this ) { continue ; } // 如果計(jì)算失敗繼續(xù)循環(huán) if (isNaN(distance)) { continue ; } // 如果更小,記錄下這個(gè)距離,并將它作為found if (distance < max_distance) { max_distance = distance; found = ele; } } // 準(zhǔn)備讓灰框落腳 var _ghostElement = getGhostElement(); // 如果找到了另外的落腳點(diǎn) if (found != null && _ghostElement.nextSibling != found.elm) { // 找到落腳點(diǎn)就先把灰框插進(jìn)去,這就是我們看到的那個(gè)灰框??康奶匦?,有點(diǎn)像吸附的感覺(jué),哈哈 found.elm.parentNode.insertBefore(_ghostElement, found.elm); if (Util.isOpera) { // Opera的現(xiàn)實(shí)問(wèn)題,要隱藏/顯示后才能刷新出變化 document.body.style.display = " none " ; document.body.style.display = "" ; } } }; // 拖拽完畢 function end_Drag() { // 拖拽完畢后執(zhí)行后面的鉤子,執(zhí)行after_Drag(),如果布局發(fā)生了變動(dòng)了就記錄到遠(yuǎn)程服務(wù)器,保存你拖拽后新的布局順序 if ( this ._afterDrag()) { // remote call to save the change } return true ; }; // 拖拽后的執(zhí)行鉤子 function after_Drag() { var returnValue = false ; // 防止閃爍 Util.hide(); // 把拖拽時(shí)的position=absolute和相關(guān)的那些style都消除 this .elm.style.position = "" ; this .elm.style.width = "" ; this .elm.style.zIndex = "" ; this .elm.style.filter = "" ; this .elm.style.opacity = "" ; // 獲取灰框 var ele = getGhostElement(); // 如果現(xiàn)在的鄰居不是原來(lái)的鄰居了 if (ele.nextSibling != this .origNextSibling) { // 把被拖拽的這個(gè)節(jié)點(diǎn)插到灰框的前面 ele.parentNode.insertBefore( this .elm, ele.nextSibling); // 標(biāo)明被拖拽了新的地方 returnValue = true ; } // 移除灰框,這是這個(gè)灰框的生命周期應(yīng)該就結(jié)束了 ele.parentNode.removeChild(ele); // 修改完畢,顯示 Util.show(); if (Util.isOpera) { // Opera的現(xiàn)實(shí)問(wèn)題,要隱藏/顯示后才能刷新出變化 document.body.style.display = " none " ; document.body.style.display = "" ; } return returnValue; }; // 可拖拽Element的原形,用來(lái)將event綁定到各個(gè)鉤子,這部分市比較通用的,netvibes也是基本完全相同的實(shí)現(xiàn) // 這部分推薦看dindin的這個(gè),也會(huì)幫助理解,http://www./page/dindin/?anchor=pro_javascript_12 var Drag = { // 對(duì)這個(gè)element的引用,一次只能拖拽一個(gè)Element obj: null , // element是被拖拽的對(duì)象的引用,elementHeader就是鼠標(biāo)可以拖拽的區(qū)域 init: function (elementHeader, element) { // 將start綁定到onmousedown事件,按下鼠標(biāo)觸發(fā)start elementHeader.onmousedown = Drag.start; // 將element存到header的obj里面,方便header拖拽的時(shí)候引用 elementHeader.obj = element; // 初始化絕對(duì)的坐標(biāo),因?yàn)椴皇莗osition=absolute所以不會(huì)起什么作用,但是防止后面onDrag的時(shí)候parse出錯(cuò)了 if (isNaN(parseInt(element.style.left))) { element.style.left = " 0px " ; } if (isNaN(parseInt(element.style.top))) { element.style.top = " 0px " ; } // 掛上空Function,初始化這幾個(gè)成員,在Drag.init被調(diào)用后才幫定到實(shí)際的函數(shù),可以參照draggable里面的內(nèi)容 element.onDragStart = new Function(); element.onDragEnd = new Function(); element.onDrag = new Function(); }, // 開(kāi)始拖拽的綁定,綁定到鼠標(biāo)的移動(dòng)的event上 start: function (event) { var element = Drag.obj = this .obj; // 解決不同瀏覽器的event模型不同的問(wèn)題 event = Drag.fixE(event); // 看看是不是左鍵點(diǎn)擊 if (event.which != 1 ) { // 除了左鍵都不起作用 return true ; } // 參照這個(gè)函數(shù)的解釋,掛上開(kāi)始拖拽的鉤子 element.onDragStart(); // 記錄鼠標(biāo)坐標(biāo) element.lastMouseX = event.clientX; element.lastMouseY = event.clientY; // 將Global的event綁定到被拖動(dòng)的element上面來(lái) document.onmouseup = Drag.end; document.onmousemove = Drag.drag; return false ; }, // Element正在被拖動(dòng)的函數(shù) drag: function (event) { // 解決不同瀏覽器的event模型不同的問(wèn)題 event = Drag.fixE(event); // 看看是不是左鍵點(diǎn)擊 if (event.which == 0 ) { // 除了左鍵都不起作用 return Drag.end(); } // 正在被拖動(dòng)的Element var element = Drag.obj; // 鼠標(biāo)坐標(biāo) var _clientX = event.clientY; var _clientY = event.clientX; // 如果鼠標(biāo)沒(méi)動(dòng)就什么都不作 if (element.lastMouseX == _clientY && element.lastMouseY == _clientX) { return false ; } // 剛才Element的坐標(biāo) var _lastX = parseInt(element.style.top); var _lastY = parseInt(element.style.left); // 新的坐標(biāo) var newX, newY; // 計(jì)算新的坐標(biāo):原先的坐標(biāo)+鼠標(biāo)移動(dòng)的值差 newX = _lastY + _clientY - element.lastMouseX; newY = _lastX + _clientX - element.lastMouseY; // 修改element的顯示坐標(biāo) element.style.left = newX + " px " ; element.style.top = newY + " px " ; // 記錄element現(xiàn)在的坐標(biāo)供下一次移動(dòng)使用 element.lastMouseX = _clientY; element.lastMouseY = _clientX; // 參照這個(gè)函數(shù)的解釋,掛接上Drag時(shí)的鉤子 element.onDrag(newX, newY); return false ; }, // Element正在被釋放的函數(shù),停止拖拽 end: function (event) { // 解決不同瀏覽器的event模型不同的問(wèn)題 event = Drag.fixE(event); // 解除對(duì)Global的event的綁定 document.onmousemove = null ; document.onmouseup = null ; // 先記錄下onDragEnd的鉤子,好移除obj var _onDragEndFuc = Drag.obj.onDragEnd(); // 拖拽完畢,obj清空 Drag.obj = null ; return _onDragEndFuc; }, // 解決不同瀏覽器的event模型不同的問(wèn)題 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_; } }; // 下面是初始化的函數(shù)了,看看上面這些東西怎么被調(diào)用 var _IG_initDrag = function (el) { // column那個(gè)容器,在google里面就是那個(gè)table布局的tbody,netvibes用的<div> Util.rootElement = el; // 這個(gè)tbody的行 Util._rows = Util.rootElement.tBodies[ 0 ].rows[ 0 ]; // 列,google是3列,其實(shí)也可以更多 Util.column = Util._rows.cells; // 用來(lái)存取可拖拽的對(duì)象 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就把它初始化為一個(gè)draggable對(duì)象 if (ele1.tagName == " DIV " ) { Util.dragArray[counter] = new draggable(ele1); counter ++ ; } } } }; // google的頁(yè)面里可以拖動(dòng)的部分的id是"t_1" // 掛載到onload,載入完畢執(zhí)行。不過(guò)實(shí)際上google沒(méi)有用onload。 // 而是寫(xiě)在頁(yè)面最下面,異曲同工吧,也許直接寫(xiě)在頁(yè)面是種怪癖,或者也有可能是兼容性考慮。 // 請(qǐng)將下面兩條被注釋掉的代碼加,到你自己下載的一個(gè)google ig頁(yè)面里面,把里面的所有其余script刪除,掛上這個(gè)js也可以拖拽了,哈哈 // _table=document.getElementById("t_1"); // window.onload = _IG_initDrag(_table); // 其實(shí)看懂這些代碼對(duì)學(xué)習(xí)javascript很有益,希望對(duì)大家能有幫助 http://www./iamtin/archive/2006/04/27/43668.html |
|
來(lái)自: nbtymm > 《JavaScript》