原生js絕對定位實現拖拽
首先我們來思考一下拖拽功能用到的事件
拖拽無非是鼠標按下點擊物體(DOM節點)
鼠標移動,物體移動
鼠標抬起,物體停止拖拽
所以這裡我們需要綁定三個事件
mousedown、mousemove、mouseup
不過通過剛才我們所思考的
mousemove和mouseup事件觸發應該是由一個鼠標按下的前提
我們可以利用一個佈爾變量儲存狀態表示當前是否鼠標按下
或者幹脆在mousedown事件處理函數中去綁定mousemove、mouseup
那麼現在我們大腦裡有瞭這樣一個實現拖拽功能函數的藍圖
(主要是邏輯,綁定事件等等兼容問題我就不具體實現瞭b( ̄▽ ̄)d)
.demo { width: 100px; height: 100px; background-color: orangered; border-radius: 50%; }
.demo { width: 100px; height: 100px; background-color: orangered; border-radius: 50%; }
var demo = document.getElementsByTagName('p'); drag(demo[0]); drag(demo[1]); drag(demo[2]);
利用我們剛剛封裝的絕對定位拖拽函數
我鼠標按下第一個球(DOM節點)的一瞬間
第二個球和第三個球跑到瞭上面
這是因為點擊節點的一瞬間,它多瞭樣式position:absolute
絕對定位會使節點脫離當前的文本流
於是沒有經過拖拽的節點2和節點3就看不到節點1瞭
(瀏覽器發生瞭reflow重排)
不僅僅這這樣,如果這個元素原來是float元素,absolute定位後float也會失效
同樣會產生這樣的問題
那麼怎樣解決這種問題呢?
最開始我的想法是創建一個一模一樣的透明節點占位
光是想一想都覺得很麻煩,(然而我不想麻煩)
反過來想一想relative或許更合適
相對定位不同於絕對定位的一點就是它不會脫離正常的文本流
而且它的left和top是相對於自己原來位置的,而不是相對於離自己最近的定位父級元素
有瞭這些想法,我們來嘗試重構函數
function drag(ele){ var oldX, oldY, newX, newY; ele.onmousedown = function(e){ this.style.position = 'relative';//元素相對定位 if(!this.style.left && !this.style.top){//第一次設置left、top為0 this.style.left = 0; this.style.top = 0; } oldX = e.clientX;//記錄初始光標相對於視窗坐標 oldY = e.clientY; document.onmousemove = function(e){ newX = e.clientX;//獲取當前新光標相對於視窗坐標 newY = e.clientY; ele.style.left = parseInt(ele.style.left) + newX - oldX + 'px';//更新 ele.style.top = parseInt(ele.style.top) + newY - oldY + 'px'; oldX = newX;//新坐標變為老坐標 oldY = newY; } document.onmouseup = function(){ document.onmousemove = null; document.onmouseup = null; } } }
這段代碼我就不細說瞭,也很容易看懂
如果你還想寫的再豐滿一些,可以添加z-index屬性
jQuery絕對定位實現拖拽
上面解釋瞭很多,這裡我就不那麼囉嗦瞭
直接上代碼,道理是一樣的
$.fn.extend({ drag: function(){ this.on('mousedown',function(e){ $(this).css('position','absolute'); var disX = e.clientX - $(this).position().left, disY = e.clientY - $(this).position().top, $self = $(this); $(document).on('mousemove',function(e){ $self.css('left',e.clientX - disX); $self.css('top',e.clientY - disY); }) $(document).on('mouseup',function(){ $(document).off(); }) }); } });
$('.demo').drag();
還記得我 jQuery框架常用的性能優化裡提到的擴展插件麼
這裡我利用瞭$.fn.extend()封裝瞭對象方法的插件
提到這裡多說一嘴,這裡drag方法內部的this是一個jQuery對象,而不是元素,與on()綁定的事件觸發函數內部不同,這裡要特別註意
但是同樣會產生上面所說的瀏覽器reflow重排的災難問題
jQuery相對定位實現拖拽
$.fn.extend({ drag: function(){ var oldX, oldY, newX, newY; this.on('mousedown',function(e){ $(this).css('position','relative'); oldX = e.clientX; oldY = e.clientY; var $self = $(this); $(document).on('mousemove',function(e){ newX = e.clientX, newY = e.clientY; $self.css('left','+=' + (newX - oldX)); $self.css('top','+=' + (newY - oldY)); oldX = newX; oldY = newY; }) $(document).on('mouseup',function(){ $(document).off(); }) }); } });
$('.demo').drag();
我也同樣利用jQuery的相對定位實現瞭拖拽
話說jQuery這個off()方法倒是很方便,不填參數把document所有事件都解除瞭