今天來實現一個可兼容的js原生拖拽,在這里面我將會講到:
1.封裝兼容性的事件系統。
2.封裝得到鼠標當前位置的系統。
3.完成拖拽的實現。
首先,我們要講到鼠標位置的獲取,講到這個,就離不開js的window.event事件了。先展開一個例子:
<div id="drag"></div>
*{margin: 0;padding: 0} #drag{ position: absolute; top: 100px; left: 200px; width: 60px;height: 60px; background-color: red; }
當我點擊紅色div的中間偏上的地方,打印他的event對象,看到里面給了好多屬性,我們來一一解釋一下。先盜一個表:
名稱 | 解釋 |
clientX | 事件屬性返回當事件被觸發時鼠標指針向對於瀏覽器頁面(或客戶區)的水平坐標。 |
clientY | 事件屬性返回當事件被觸發時鼠標指針向對於瀏覽器頁面(客戶區)的垂直坐標。 |
screenX | 事件屬性可返回事件發生時鼠標指針相對於屏幕的水平坐標。 |
screenY | 事件屬性可返回事件發生時鼠標指針相對於屏幕的垂直坐標。 |
offsetX | 事件發生的地點在事件源元素的坐標系統中的 x 坐標(ie)。 |
offsetY | 事件發生的地點在事件源元素的坐標系統中的 y 坐標(ie)。 |
x | 事件發生的位置的 x 坐標, 它相對於用CSS動態定位的最內層包容元素(ie,~pageX)。 |
y | 事件發生的位置的 y 坐標, 它相對於用CSS動態定位的最內層包容元素(ie,~pageY)。 |
pageX | 鼠標指針的位置,相對於文檔的左邊緣(firefox,~x)。 |
pageY | 鼠標指針的位置,相對於文檔的上邊緣(firefox,~y)。 |
layerX | 鼠標相比較於當前坐標系的位置(firefox,~offsetX)。 |
layerY | 鼠標相比較於當前坐標系的位置(firefox,~offsetY)。 |
然后把剛剛的圖截完:
我這個div的寬60,高60,left:200,top:100;
clientX/Y:可以看到它的clientX是229,根據表里的描述,它是鼠標指針到瀏覽器頁面,不包含瀏覽器的工具欄什么的。因為我點的大約是div的中部最上面,所以它的值就等於top值:100px。X的229就是200的left加上了一半的寬30---就是230左右。(由於我隨便點的,不准確,但是基本接近)。
screenX/Y:相對於屏幕的,可以看到X沒怎么變,Y變成了163。就是因為它有61px的工具欄和導航欄的高度。so,你把瀏覽器縮小,它還是相對於屏幕,而不是瀏覽器,你會發現它就和上一個差太多了。
offsetX/Y:這個就比較有用了,鼠標相對於這個元素的位置,offset嘛,就是相對了。可以看到x是29,y是2,因為我大約點的中間。
x/y: 相對於它父元素的位置,相當於firefox中的pageX、pageY坐標。當父元素是document的時候就跟clientX/Y一樣。因為它的父元素就是document。。。所以跟上面那幾個一樣。說是ie特有,不過我chrome上面也有這個屬性。但是我chrome的x,y的屬性就算你有父元素還是等於clientX/Y,高版本ie也是如此。大家就把它記作clientX/Y就好。
layerX/Y:其實就是offsetX/Y,不過offsetX/Y是ie的屬性,layerX/Y是firefox支持的。chrome我這個版本都支持了。
pageX/Y:鼠標指針的位置,相對於文檔的左邊緣和上邊緣,是firefox中支持的,相似於ie中的x和y。和client區別就是如果有滾動條,他是大於client的
總結一下:clientX/Y== X/Y==無滾動條下pageX/Y offsetX/Y==layerX/Y screenX/Y相對於屏幕,就它自己。
那回到我們取鼠標坐標,怎么辦呢,看能用到哪個屬性:clientX/Y這就是鼠標真正的坐標,相對於本頁面的坐標。然后offsetX/Y是相對於div的偏移。那么這個div的位置就是pageX/Y-offsetX/Y;大家打印一下,就能知道x總是返回100,y返回200;
那我們開始寫取坐標的位置的兼容js:
function getPosition(e){ e=e||window.event; var target=e.target?e.target:e.srcElement; //兼容ie的srcElement var pos_rel={}; var pos_offset={}; pos_rel={ x:e.pageX?e.pageX:e.clientX+(document.body.scrollLeft||document.documentElement.scrollLeft),//clientX加上滾動條滾動的位置 y:e.pageY?e.pageY:e.clientY+(document.body.scrollTop||document.documentElement.scrollTop) } pos_offset={ x:e.offsetX?e.offsetX:e.layerX, x:e.offsetY?e.offsetY:e.layerY } return { rel:pos_rel, offset:pos_offset } }
取到這個位置就好做拖拽了。就是讓鼠標點擊的時候記錄相對於div的x,y,鼠標移動設置div的left和top,記得減去剛剛記錄的offsetX/Y。鼠標松開就設置不能移動。
好,在這之前先說一下封裝事件系統
事件系統就是為了ie不支持addEventListener才做的封裝兼容
別的瀏覽器可以addEventListener和removeEventListener,而ie的是attachEvent和detachEvent。ie的這個和addEventListener用法是相似的,需要注意的一點就是要把處理事件保存用以解除監聽。
我上一下我寫的事件封裝:
var addEvent=function(el,event,cb){ if (el.addEventListener) { el.addEventListener(event,cb); }else if(el.attachEvent){ el.attachEvent(event,cb); } return cb; //注意這里返回處理函數 } var cancelEvent=function(el,event,cb){ if (el.removeEventListener) { el.removeEventListener(event,cb); }else if(el.detachEvent){ el.detachEvent('on'+event,cb) } }
使用就是
var handle=addEvent(document,"click",function(e){ console.log("be clicked") })
解除事件監聽:
cancelEvent(document,"click",handle)
這里我沒有寫第三個參數,默認冒泡添加解除事件,有需要也可以多加參數。
好,那么開始寫拖拽吧:
首先點擊這個div記錄當前offsetX,Y
var isDrag=false;var ofx,ofy; var mousedownH=addEvent(drag,"mousedown",function(e){ isDrag=true; ofx=getPosition().offset.x; ofy=getPosition().offset.y; })
然后設置div位置:
var mousemoveH=addEvent(document,"mousemove",function(e){ //注意這里的mousemove和mouseup都是在document上的事件,這是為了避免你鼠標移動太快,移出了這個div,就沒有move事件觸發了。 if(isDrag) { var relX=getPosition().rel.x-ofx; var relY=getPosition().rel.y-ofx; drag.style.left=relX+"px"; drag.style.top=relY+"px"; }; })
最后鼠標抬起取消它的可移動:
var mouseupH=addEvent(document,"mouseup",function(e){ isDrag=false; })
搞定。
最后把全部代碼分享,大家復制就可以用了
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>js拖拽</title> <style type="text/css"> *{margin: 0;padding: 0} #drag{ position: absolute; top: 100px; left:200px; width: 60px;height: 60px; background-color: red; } </style> </head> <body> <div id="drag"></div> </body> <script type="text/javascript"> var drag=document.getElementById('drag'); var click1=document.getElementById('click'); var can=document.getElementById('can'); var addEvent=function(el,event,cb){ if (el.addEventListener) { el.addEventListener(event,cb); }else if(el.attachEvent){ el.attachEvent(event,cb); } return cb; } var cancelEvent=function(el,event,cb){ if (el.removeEventListener) { el.removeEventListener(event,cb); }else if(el.detachEvent){ el.detachEvent('on'+event,cb) } } function getPosition(e){ e=e||window.event; var target=e.target?e.target:e.srcElement; var pos_rel={}; var pos_offset={}; pos_rel={ x:e.pageX?e.pageX:e.clientX+(document.body.scrollLeft||document.documentElement.scrollLeft), y:e.pageY?e.pageY:e.clientY+(document.body.scrollTop||document.documentElement.scrollTop) } pos_offset={ x:e.offsetX?e.offsetX:e.layerX, x:e.offsetY?e.offsetY:e.layerY } return { rel:pos_rel, offset:pos_offset } } var isDrag=false;var ofx,ofy; var mousedownH=addEvent(drag,"mousedown",function(e){ isDrag=true; ofx=getPosition().offset.x; ofy=getPosition().offset.y; }) var mousemoveH=addEvent(document,"mousemove",function(e){ if(isDrag) { var relX=getPosition().rel.x-ofx; var relY=getPosition().rel.y-ofx; drag.style.left=relX+"px"; drag.style.top=relY+"px"; }; }) var mouseupH=addEvent(document,"mouseup",function(e){ isDrag=false; }) </script> </html>