一、實現的效果是在限制范圍內拖拽div+吸附+事件捕獲。
這里需要理解的是事件捕獲,這個事件捕獲也是為了兼容div在拖拽過程中,文本不被選中這個問題。
如此良辰美景,拖拽也可以很灑脫哈。先看看圖,
二、一步步的實現這個拖拽過程的幾個要求
(一)拖拽起來
里面的邊框是表示頁面哦(我們的屏幕所能看到的東東)。
獲取移動距離的思路:
記錄鼠標按下和鼠標抬起兩次的坐標,然后相減,再加上div跟邊緣之間的間距。就得到移動距離。
之前我也在這里困惑了,不明白為什么還要再加上offsetLeft。原因就是clientX獲取到的是數值是不加上div跟邊緣的距離,不是marin,也不是padding,而是瀏覽器渲染的問題。
[下面是我自己的理解:
終於明白這個移動距離是如何計算出來的:
將式子化簡之后,得到的就是移動后的Div clientX-移動前clientX,然后再加上offsetLeft,因為這個clientX是沒有把邊緣計算下去,為了獲取准確的數值,要把瀏覽器默認的邊緣計算下去。
如圖所以:鼠標移動過的距離就是我用紅色畫出部分再加上div跟邊緣之間的offsetLeft(X軸方向)和offsetTop(Y軸方向)。
如果上面式子不好理解,就把他化簡之后來看,就明白了。]
距離獲取完成。
現在就可以通過鼠標的三個事件onmousedown、onmousemove、onmouseup來拖拽鼠標。當鼠標移動時,就不斷地更改div的left和top屬性
oDiv2.style.left = l +'px';
Div2.style.top = t +'px';
最后,當鼠標抬起時,要釋放onmousedown和onmousemove事件。
this.onmousedown = null;
this.onmousemove = null;
(二)邊緣吸附
邊緣吸附的原理so easy。
給一個判斷條件,當div運動到距離上下左右邊緣的距離小於某一個值時,這時就把left和top的值更改為邊緣的值。這樣div就貼到邊緣上去。
var l1= oDiv1.offsetWidth - oDiv2.offsetWidth; //限制小div在大div中拖拽,計算能拖拽的max距離 var t1 = oDiv1.offsetHeight - oDiv2.offsetHeight; if(l > l1-50) { l = l1; } if(l < 50) { l = 0; } if(t > t1-50) { t = t1; } if(t < 50) { t = 0; }
(三)拖拽過程不被文字選中
div在拖拽過程中,在div中的文本文字總是會被選中,為了解決這個問題,要使用一個叫做事件捕獲的知識。
1、先理解一下什么是事件捕獲
是跟事件冒泡相反的一種模型。事件捕獲的是最后獲得事件的是最小的子元素。事件冒泡最后獲得事件的是父元素。
之所以在拖拽過程中,div中的文字會被選中就是因為我沒有處理好事件冒泡的問題。要解決這個問題,解鈴還須系鈴人,就把事件冒泡的問題處理好久ok。
if(oDiv2.setCapture) //IE
{
document.onmousemove = moveFn;
document.onmouseup = upFn;
oDiv2.setCapture(); //事件捕獲后,所有事件都集中到這個div
return false; //FF、Chrome、IE9
}else //FF、chrome
{
document.onmousemove = moveFn; //!!!!根源所在,在優化版1中,設置為oDiv2.onmousemove時拖拽一次后無法再拖拽
document.onmouseup = upFn;
}
記得事件捕獲后,當鼠標抬起時,也好釋放
oDiv2.releaseCapture();
三、div拖拽的詳細代碼

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>限制范圍內拖拽</title> <style> * { margin: 0; padding: 0; } #div1 { width: 500px; height: 500px; background: #CCC; position: relative; } #div2 { width: 100px; height: 100px; background: green; position: absolute; left: 0; top: 0; } </style> <script> window.onload = function() { var oDiv1 = document.getElementById('div1'); var oDiv2 = document.getElementById('div2'); var disX,disY; /*--------------開始拖拽div2-----------------*/ oDiv2.onmousedown = function(evt) //oDiv2.onmousedown表示按下這個對象,, document.onmouseup整個文檔對象(這里把div改成document是防止弄丟div) { var oEvent = evt || window.event; //evt兼容FF/Chrome disX = oEvent.clientX - oDiv2.offsetLeft; //-oDiv2.offsetLeft的距離是為了減去div與視口邊框的距離 disY = oEvent.clientY - oDiv2.offsetTop; if(oDiv2.setCapture) //IE { document.onmousemove = moveFn; document.onmouseup = upFn; oDiv2.setCapture(); //事件捕獲后,所有事件都集中到這個div return false; //FF、Chrome、IE9 }else //FF、chrome { document.onmousemove = moveFn; //!!!!根源所在,在優化版1中,設置為oDiv2.onmousemove時拖拽一次后無法再拖拽 document.onmouseup = upFn; } function moveFn(evt) //把document重新改為div,利用setCapture事件捕獲,把事件都集中在一個物體上 { var oEvent = evt || window.event; var l = oEvent.clientX - disX; //計算鼠標移過的距離 var t = oEvent.clientY - disY; var l1= oDiv1.offsetWidth - oDiv2.offsetWidth; //限制小div在大div中拖拽,計算能拖拽的max距離 var t1 = oDiv1.offsetHeight - oDiv2.offsetHeight; if(l > l1-50) { l = l1; } if(l < 50) { l = 0; } if(t > t1-50) { t = t1; } if(t < 50) { t = 0; } oDiv2.style.left = l +'px'; oDiv2.style.top = t +'px'; } function upFn() { this.onmousedown = null; this.onmousemove = null; if(oDiv2.releaseCapture) //如果事件捕獲存在,則釋放事件捕獲 { oDiv2.releaseCapture(); } } return false; //阻止瀏覽器默認事件 }; }; </script> </head> <body> <div id="div1">使用了事件捕獲后,現在拖拽div中的問題可不應該被選中了哦</div> <div id="div2">helloworld helloworld</div> </body> </html>
[題外話:優化版拖拽再次無法拖拽(已解決:原因是document和oDiv2.onmousemove的問題)]