這兩天太忙了,沒時間寫Blog,業余時間又扒了一次google的拖拽,對比了netvibes的,差不多,讓后就寫了個注釋,順便學習。
google用了混淆,js里面的命名比較暈。我花了些時間把他們翻譯成容易看懂的命名,并且作了注釋,希望對大家有幫助。
可以這里下載我的例子:
http://www.tkk7.com/Files/iamtin/google_drag.rar
google用了混淆,js里面的命名比較暈。我花了些時間把他們翻譯成容易看懂的命名,并且作了注釋,希望對大家有幫助。
可以這里下載我的例子:
http://www.tkk7.com/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很有益,希望對大家能有幫助
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很有益,希望對大家能有幫助