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