javascript 拖放效果實現代碼

來源:互聯網
上載者:User

有許多理由讓你在頁面中加入拖放的功能,其中最簡單的理由是重新組織資料。舉個例子,你可能希望使用者能夠重組一系列的頁面元素,通過放置一個input或 select組件在各個元素的旁邊來代表它們的順序是一種解決方案,使該組元素可以被拖放是一種替代方案。或者也許你想在網站上擁有一個可以被使用者移動的導航視窗。這些都是使用拖放功能的簡單理由,因為你能夠實現!
在你的網頁上實現拖放的效果並不是很複雜。首先,我們知道滑鼠的位置,然後我們需要瞭解使用者什麼時候點擊一個元素,以至於我們知道要準備開始拖動它,最後我們要移動這個元素。

捕獲滑鼠的移動
第一步,我們需要擷取滑鼠的座標,通過一個函數並賦給document.onmousemove可以實現這一功能:

代碼 複製代碼 代碼如下:document.onmousemove = mouseMove;
function mouseMove(ev) {
ev = ev || window.event;
var mousePos = mouseCoords(ev);
}
function mouseCoords(ev) {
if(ev.pageX || ev.pageY) {
return {x:ev.pageX, y:ev.pageY};
}
return {
x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
y:ev.clientY + document.body.scrollTop - document.body.clientTop
};
}

首先我們需要解釋一下event對象。不論你什麼時候移動、點擊滑鼠,或按鍵,等等,一個事件都會發生。在IE中,這個事件是全域的,它被儲存在 window.event中,對於Firefox,及其他的瀏覽器來說,這個事件將被傳遞到任何指向這個頁面動作的函數中。因此,我們使 document.onmousemove指向滑鼠移動的函數,滑鼠移動的函數獲得事件對象。
上述代碼中,ev在所有瀏覽器環境中都包含了event對象。在Firefox裡,"||window.event"將被忽略,因為它已經包含事件。在IE中,ev的值為空白,以至於需要將它的值設定為window.event。
本文中我們需要多次捕獲到滑鼠的座標,因此我們寫了一個mouseCoords方法,它有一個參數:event。
我們要再次討論IE和其他瀏覽器之間的差異。Firefox和其他的瀏覽器使用event.pageX和event.pageY來表示滑鼠相對於 document文檔的位置。如果你有一個500*500的視窗,並且滑鼠位於視窗中間,那麼pageX和pageY的值將都是250。如果你將視窗向下滾動500象素,pageY的值為750。
如此相反的是,微軟的IE使用event.clientX和event.clientY來表示滑鼠相對於window視窗的位置,而不是當前 document文檔。在相同的例子中,如果將滑鼠放置於500*500視窗的中間,clientX和clientY值將均為250。如果向下滾動頁面, clientY將仍為250,因為它是相對於window視窗來測量,而不是當前的document文檔。因此,在滑鼠位置中,我們應該引入 document文檔body地區的scrollLeft和scrollTop屬性。最後,IE中document文檔實際並不在(0,0)的位置,在它周圍有一個小(通常有2px)邊框,document.body.clientLeft和document.body.clientTop包含了這個邊框的寬度,從而還需要在滑鼠位置中引入它們。
幸運的是,現在我們擁有了mouseCoords函數,不用再為擷取滑鼠位置擔心了。

捕獲滑鼠的點擊
下一步,我們必須知道滑鼠何時點擊及何時釋放。如果我們跳過這一步,只要你的滑鼠移動經過這些元素時,都將產生拖動這些元素的效果,這是令人討厭並違反人的直覺的。
在這裡,有兩個函數可以協助我們:onmousedown和onmouseup。先前我們已將document.onmousemove指向一個函數,因此從邏輯上似乎應該使document.onmousedown和document.onmouseup都指向函數。如果我們讓 document.onmousedown指向一個函數,那麼這個函數將會因為滑鼠點擊任何元素而執行:文本、映像、表格,等等。我們只想頁面中特定的元素具有被拖放的功能,因此,我們可以通過如下方法實現:

代碼 複製代碼 代碼如下:document.onmouseup = mouseUp;
var dragObject = null;
function makeClickable(object) {
object.onmousedown = function() {
dragObject = this;
}
}
function mouseUp(ev) {
dragObject = null;
}

我們現在有了一個變數dragObject,包含了你點擊的任何元素。當你釋放滑鼠的時候,dragObject被設定為空白,從而在dragObject非空的時候,我們需要進行拖動操作。

移動元素
我們現在已經知道如何捕獲滑鼠移動和點擊。接下來需要做的就是移動任何我們想拖動的元素。首先,將一個元素準確移動到頁面上我們想要的位置,該元素樣式表的position值必須為absolute,這意味著你可以設定它的style.top或style.left,測量值相對於頁面的左上方,因為我們所有的滑鼠移動都是相對於頁面左上方的,通常都是這樣。
一旦我們設定了item.style.position='absolute',接下來就需要改變該元素top和left的位置,使它移動!

代碼 複製代碼 代碼如下:document.onmousemove = mouseMove;
document.onmouseup = mouseUp;
var dragObject = null ;
var mouseOffset = null ;
function getMouseOffset(target, ev) {
ev = ev || window.event;
var docPos = getPosition(target);
var mousePos = mouseCoords(ev);
return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y} ;
}
function getPosition(e) {
var left = 0;
var top = 0;
while (e.offsetParent) {
left += e.offsetLeft;
top += e.offsetTop;
e = e.offsetParent;
}
left += e.offsetLeft;
top += e.offsetTop;
return {x:left, y:top} ;
}
function mouseMove(ev) {
ev = ev || window.event;
var mousePos = mouseCoords(ev);
if (dragObject) {
dragObject.style.position = 'absolute';
dragObject.style.top = mousePos.y - mouseOffset.y;
dragObject.style.left = mousePos.x - mouseOffset.x;
return false ;
}
}
function mouseUp() {
dragObject = null ;
}
function makeDraggable(item) {
if (!item) return ;
item.onmousedown = function (ev) {
dragObject = this ;
mouseOffset = getMouseOffset(this, ev);
return false ;
}
}

你會注意到這些代碼是以我們前面的例子為基礎的(參考上篇文章),將它們放置在一起,你將能夠隨意的去移動元素。
當我們點擊一個元素時,儲存了另外的一個變數,mouseOffset。mouseOffset簡單的包含了我們點擊元素的位置資訊。如果我們有一張 20*20px的映像,然後點擊映像的中間,mouseOffset應該是{x:10, y:10}。如果我們點擊映像的左上方,mouseOffset應為{x:0, y:0}。我們在滑鼠移動後的位置資訊中用到它。如果我們沒有儲存這個值,不論你點擊元素的哪一個位置,元素相對於滑鼠的位置都將會是相同的。
mouseOffset函數用到了另外一個函數getPosition。getPosition目的是返回元素相對於documemt文檔的座標位置。如果我們簡單的去讀取item.offsetLeft或item.style.left,得到的將是元素相對於它父元素的位置,而不是document文檔的。在我們的指令碼中,所有的元素都是相對於document文檔的,因此需要這樣做。
要完成擷取元素相對於document文檔位置的工作,getPosition從它自身的父級開始,迴圈擷取它的left和top的值並累加,這樣我們就得到了我們想要的元素距文檔頂部和左側的累計值。
當我們擷取了這條資訊並移動滑鼠的時候,mouseMove開始運行。首先我們需要保證item.style.position值為absolute,接著,我們將元素移動到任何一個地方,滑鼠位置都會減去我們之前記錄的滑鼠相對於元素的位移量。當滑鼠釋放時,dragObject將被設定為null,並且mouseMove函數不再做任何事情。

放置元素
我們前面的例子已經處理了這個問題,僅僅是拖動一個元素,然後將它放下。然後,在我們放下元素的時候通常還有其他的目的,我們以拖動元素到記憶體回收站為例,或我們可能想讓該元素和頁面中某個特定的地區對齊。
不幸的是我們在這裡進入了一個相對主要的問題。因為我們正在移動的元素總是直接處於我們的滑鼠下,而不可能去引發mouseover、 mousedown、mouseup或滑鼠對頁面中其他元素的操作。如果你移動一個元素到記憶體回收站,你的滑鼠會一直在移動元素的上方,而不是記憶體回收站。
那麼我們該如何處理這個問題呢?這裡有幾種解決方案。在前面所提到的mouseOffset的目的是保證元素總是在滑鼠下方正確的位置,如果你忽視了這點,然後總是使得元素在滑鼠的右下方,你的滑鼠將不會被你正在拖動的元素所隱藏,我們也不會碰到問題。但事實上往往不會這樣,為了美觀我們通常要保持元素在滑鼠的下方。
另外一種選擇是不移動你正在拖動的元素,你可以改變滑鼠樣式,來告訴使用者你正在拖動一個元素,直到你將它放置到某個地方。這解決了我們的問題,但是帶來了和前面一種方案面臨的同樣問題:美觀。
我們最後的一種解決方案既不影響你正在移動的元素,也不影響移動終點位置上的元素(例如記憶體回收站)。不幸的是,這比前面兩種解決方案的難度更大。我們將要做的是獲得一組我們要放置的目標,當滑鼠釋放時,我們手工檢查當前滑鼠相對於每個目標的位置,看滑鼠是否釋放在這個目標中某一個目標的位置上,如果是的,我們就知道我們已經將元素放置在我們的目標上了。

代碼 複製代碼 代碼如下:/*
All code from the previous example is needed with the exception
of the mouseUp function which is replaced below
*/
var dropTargets = [];
function addDropTarget(dropTarget) {
dropTargets.push(dropTarget);
}
function mouseUp(ev) {
ev = ev || window.event;
var mousePos = mouseCoords(ev);
for (var i = 0; i < dropTargets.length; i ++) {
var curTarget = dropTargets[i];
var targPos = getPosition(curTarget);
var targWidth = parseInt(curTarget.offsetWidth);
var targHeight = parseInt(curTarget.offsetHeight);
if (
(mousePos.x > targPos.x) &&
(mousePos. < (targPos.x + targWidth)) &&
(mousePos.y > targPos.y) &&
(mousePos.y < (targPos.y + targHeight))) {
// dragObject was dropped onto curTarget!
}
}
dragObject = null ;
}

這個例子中當滑鼠釋放時,我們迴圈每個可能放置元素的目標,如果滑鼠指標在目標上,我們則擁有了一個放置元素的事件,通過滑鼠橫座標大於目標元素左側橫座標(mousePos.x>targPos.x),小於目標元素右側橫座標(mousePos.x<(targPos.x+ targWidth))來判定,對於Y座標我們做同樣的判斷。如果所有的這些值都返回true,那麼我們的滑鼠就是在目標元素的範圍內。

整合所有的功能
最後我們使用所有代碼片斷,來建立一個完整的拖放函數指令碼。我們所要做的第一件事情是DOM操作,如果你對此並不十分熟悉,可以閱讀《JavaScript Primer on DOM Manipulation》。
接下來的代碼建立容器和容器組,使得在這些容器中可以拖動每個元素,這在本文第二個demo的基礎上來完成。這段代碼能夠用來重新規劃元素的順序,將導航視窗放在頁面的左側或右側,或再加入你所能想到的其他的功能。
我們將使用虛擬碼來一步步進行講解,將真實的代碼通過注釋的方式留給讀者查看。
1.當文檔第一次被載入時,我們建立一個名為dragHelper的DIV標籤,當我們開始移動一個元素的時候,dragHelper將成為一個隱藏元素,可以四處移動。真實的元素並不會被拖動,僅僅使用insertBefore和appendChild來移動。我們在開始的時候隱藏dragHelper。
2.我們建立mouseDown和mouseUp函數。起初,所有的這些函數都假設記錄了滑鼠按鍵的狀態,以至於iMouseDown變數在滑鼠按下的時候為true,沒有按下的時候為false。
3.我們建立一個全域變數DragDrops,以及一個函數CreateDragContainer。DragDrops包含一組相互關聯的容器。傳入CreateDragContainer的任何變數(代表容器)被組織成一個新的集合,使元素能夠在這些容器間自由移動。通過setAttribute,CreateDragContainer函數同樣將各容器中的元素繫結在一起。
4.現在我們的代碼知道每個元素所在的集合,現在來看mouseMove函數。mouseMove函數首先設定了一個變數target,表示滑鼠下面的目標元素,如果這個元素在集合(用getAttribute判斷)中就繼續下面操作:
4.1.首先,在必要的時候,我們運行一個簡單的指令碼來改變目標元素的class屬性,這樣就創造了一個翻動的效果。
4.2.然後我們檢查滑鼠是否點擊(因為我們的代碼已經運行到這裡),如果事件發生:
4.2.1.設定變數curTarget為當前元素。
4.2.2.記錄元素當前在文檔中的位置,以便在需要的時候可以將它的值取回。
4.2.3.將當前元素複製到dragHelper,使得我們能夠移動元素的隱藏備份。
4.2.4.因為在dragHelper中我們完全擁有了拖動元素的一個備份,這個元素會始終在滑鼠下,我們必須移除dragObj屬性,讓代碼知道dragObj已不在集合中。
4.2.5.我們快速記錄集合中每個元素當前的位置、寬度和高度。當元素第一次開始被拖動時,我們僅需做一次這種工作,否則每當滑鼠移動的時候我們都必須做一次,甚至一秒內幾百次。
4.3.如果滑鼠沒有點擊,要麼我們和之前擁有同樣的目標元素,要麼沒有目標元素,不論哪種情況我們都不會做任何事情。
5.現在我們檢查curTarget變數。curTarget應該僅包含一個被拖動的對象,因此如果它存在,表示我們正在拖動一個元素:
5.1.移動隱藏DIV到滑鼠,這個元素和文章前面所建立的元素一樣能夠被拖動。
5.2.然後我們檢查滑鼠是否存在於當前集合中每個容器中。
5.2.1.如果滑鼠在某個容器中,我們檢查容器中的每個元素,查看我們正拖動的元素屬於哪個位置。
5.2.2.然後我們將所拖動的元素放置在容器中另一個元素的前面,或容器的最後位置。
5.2.3.最後我們確定元素可見。
6.剩下的事情就是捕獲mouseUp事件:
6.1.首先需要隱藏dragHelper:它不再被需要,因為我們沒有拖動任何東西。
6.2.如果拖動的元素是可見的,它已經存在於任何它所屬的容器中,所有工作已完成。
6.3.如果拖動的元素不可見,我們將它放回它原來所在的地方。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.