用Javascript實現(xiàn)網(wǎng)頁表單鼠標(biāo)拖拽
當(dāng)前位置:點晴教程→知識管理交流
→『 技術(shù)文檔交流 』
怎么用javascript進行拖拽
Javascript的特點是dom的處理與網(wǎng)頁效果,大多數(shù)情況我們只用到了這個語言的最簡單的功能,比如制作圖片輪播/網(wǎng)頁的tab等等,這篇文章將向你展示如何在自己的網(wǎng)頁上制作拖拽. 有很多理由讓你的網(wǎng)站加入拖拽功能,最簡單的一個是數(shù)據(jù)重組.例如:你有一個序列的內(nèi)容讓用戶排序,用戶需要給每個條目進行輸入或者用select選擇,替代前面這個方法的就是拖拽.或許你的網(wǎng)站也需要一個用戶可以拖動的導(dǎo)航窗口!那么這些效果都是很簡單:因為你可以很容易的實現(xiàn)! 網(wǎng)頁上實現(xiàn)拖拽其實也不是很復(fù)雜.第一你需要知道鼠標(biāo)坐標(biāo),第二你需要知道用戶鼠標(biāo)點擊一個網(wǎng)頁元素并實現(xiàn)拖拽,最后我們要實現(xiàn)移動這個元素。 獲取鼠標(biāo)移動信息 第一我們需要獲取鼠標(biāo)的坐標(biāo).我們加一個用戶函數(shù)到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 }; } 你首先要聲明一個evnet對象.不論何時你移動鼠標(biāo)/點擊/按鍵等等,會對應(yīng)一個event的事件.在Internet Explorer里event是全局變量,會被存儲在window.event里. 在firefox中,或者其他瀏覽器,event事件會被相應(yīng)的自定義函數(shù)獲取.當(dāng)我們將mouseMove函數(shù)賦值于document.onmousemove,mouseMove會獲取鼠標(biāo)移動事件. (ev = ev || window.event) 這樣讓ev在所有瀏覽器下獲取了event事件,在Firefox下"||window.event"將不起作用,因為ev已經(jīng)有了賦值.在MSIE下ev是空的,所以ev將設(shè)置為window.event. 因為我們在這篇文章中需要多次獲取鼠標(biāo)坐標(biāo),所以我們設(shè)計了mouseCoords這個函數(shù),它只包含了一個參數(shù),就是the event. 我們需要運行在MSIE與Firefox為首的其他瀏覽器下.Firefox以event.pageX和event.pageY來代表鼠標(biāo)相應(yīng)于文檔左上角的位置.如果你有一個500*500的窗口,而且你的鼠標(biāo)在正中間,那么paegX和pageY將是250,當(dāng)你將頁面往下滾動500px,那么pageY將是750.此時pageX不變,還是250. MSIE和這個相反,MSIE將event.clientX與event.clientY來代表鼠標(biāo)與ie窗口的位置,并不是文檔.當(dāng)我們有一個500*500的窗口,鼠標(biāo)在正中間,那么clientX與clientY也是250,如果你垂直滾動窗口到任何位置,clientY仍然是250,因為相對ie窗口并沒有變化.想得到正確的結(jié)果,我們必須加入scrollLeft與scrollTop這兩個相對于文檔鼠標(biāo)位置的屬性.最后,由于MSIE并沒有0,0的文檔起始位置,因為通常會設(shè)置2px的邊框在周圍,邊框的寬度包含在document.body.clientLeft與clientTop這兩個屬性中,我們再加入這些到鼠標(biāo)的位置中. 很幸運,這樣mouseCoords函數(shù)就完成了,我們不再為坐標(biāo)的事操心了. 捕捉鼠標(biāo)點擊 下次我們將知道鼠標(biāo)何時點擊與何時放開.如果我們跳過這一步,我們在做拖拽時將永遠(yuǎn)不知道鼠標(biāo)移動上面時的動作,這將是惱人的與違反直覺的. 這里有兩個函數(shù)幫助我們:onmousedown與onmouseup.我們預(yù)先設(shè)置函數(shù)來接收document.onmousemove,這樣看起來很象我們會獲取document.onmousedown與document.onmouseup.但是當(dāng)我們獲取document.onmousedown時,我們同時獲取了任何對象的點擊屬性如:text,images,tables等等.我們只想獲取那些需要拖拽的屬性,所以我們設(shè)置函數(shù)來獲取我們需要移動的對象. 移動一個元素 我們知道了怎么捕捉鼠標(biāo)移動與點擊.剩下的就是移動元素了.首先,要確定一個明確的頁面位置,css樣式表要用'absolute'.設(shè)置元素絕對位置意味著我們可以用樣式表的.top和.left來定位,可以用相對位置來定位了.我們將鼠標(biāo)的移動全部相對頁面top-left,基于這點,我們可以進行下一步了. 當(dāng)我們定義item.style.position='absolute',所有的操作都是改變left坐標(biāo)與top坐標(biāo),然后它移動了。 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; } } 你會注意到這個代碼幾乎是前面的全集,將前面的合在一起就實現(xiàn)了拖拽效果了. 當(dāng)我們點擊一個item時,我們就獲取了很多變量,如鼠標(biāo)位置,鼠標(biāo)位置自然就包含了那個item的坐標(biāo)信息了.如果我們點擊了一個20*20px圖像的正中間,那么鼠標(biāo)的相對坐標(biāo)為{x:10,y:10}.當(dāng)我們點擊這個圖像的左上角那么鼠標(biāo)的相對坐標(biāo)為{x:0,y:0}.當(dāng)我們點擊時,我們用這個方法取得一些鼠標(biāo)與圖片校對的信息.如果我們不能加載頁面item,那么信息將是document信息,會忽略了點擊的item信息. mouseOffset函數(shù)使用了另一個函數(shù)getPosition.getPosition的作用是返回item相對頁面左上角的坐標(biāo),如果我們嘗試獲取item.offsetLeft或者item.style.left,那么我們將取得item相對與父級的位置,不是整個document.所有的腳本我們都是相對整個document,這樣會更好一些. 為了完成getPosition任務(wù),必須循環(huán)取得item的父級,我們將加載內(nèi)容到item的左/上的位置.我們需要管理想要的top與left列表. 自從定義了mousemove這個函數(shù),mouseMove就會一直運行.第一我們確定item的style.position為absolute,第二我們移動item到前面定義好的位置.當(dāng)mouse點擊被釋放,dragObject被設(shè)置為null,mouseMove將不在做任何事. Dropping an Item 前面的例子目的很簡單,就是拖拽item到我們希望到的地方.我們經(jīng)常還有其他目的如刪除item,比如我們可以將item拖到垃圾桶里,或者其他頁面定義的位置. 很不幸,我們有一個很大的難題,當(dāng)我們拖拽,item會在鼠標(biāo)之下,比如mouseove,mousedown,mouseup或者其他mouse action.如果我們拖拽一個item到垃圾桶上,鼠標(biāo)信息還在item上,不在垃圾桶上. 怎么解決這個問題呢?有幾個方法可以來解決.第一,這是以前比較推薦的,我們在移動鼠標(biāo)時item會跟隨鼠標(biāo),并占用了mouseover/mousemove等鼠標(biāo)事件,我們不這樣做,只是讓item跟隨著鼠標(biāo),并不占用mouseover等鼠標(biāo)事件,這樣會解決問題,但是這樣并不好看,我們還是希望item能直接跟在mouse下. 另一個選擇是不做item的拖拽.你可以改變鼠標(biāo)指針來顯示需要拖拽的item,然后放在鼠標(biāo)釋放的位置.這個解決方案,也是因為美學(xué)原因不予接受. 最后的解決方案是,我們并不去除拖拽效果.這種方法比前兩種繁雜許多,我們需要定義我們需要釋放目標(biāo)的列表,當(dāng)鼠標(biāo)釋放時,手工去檢查釋放的位置是否是在目標(biāo)列表位置上,如果在,說明是釋放在目標(biāo)位置上了. /* 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.x < (targPos.x + targWidth)) && (mousePos.y > targPos.y) && (mousePos.y < (targPos.y + targHeight))){ // dragObject was dropped onto curTarget! } } dragObject = null; } 鼠標(biāo)釋放時會去取是否有drop屬性,如果存在,同時鼠標(biāo)指針還在drop的范圍內(nèi),執(zhí)行drop操作.我們檢查鼠標(biāo)指針位置是否在目標(biāo)范圍是用(mousePos.x>targetPos.x),而且還要符合條件(mousePos.x<(targPos.x + targWidth)).如果所有的條件符合,說明指針確實在范圍內(nèi),可以執(zhí)行drop指令了. Pulling It All Together 最后我們擁有了所有的drag/drop的腳本片斷!下一個事情是我們將創(chuàng)建一個DOM處理.如果你不是很熟悉,請先閱讀我的JavaScript Primer on DOM Manipulation. 下面的代碼將創(chuàng)建container(容器),而且使任何一個需要drag/drop的item變成一個容器的item.代碼在這個文章第二個demo的后面,它可以用戶記錄一個list(列表),定為一個導(dǎo)航窗口在左邊或者右邊,或者更多的函數(shù)你可以想到的. 下一步我們將通過"假代碼"讓reader看到真代碼,下面為推薦: 1、當(dāng)document第一次載入時,創(chuàng)建dragHelper DIV.dragHelper將給移動的item加陰影.真實的item沒有被dragged,只是用了insertBefor和appendChild來移動了,我們隱藏了dragHelper 2、有了mouseDown與mouseUp函數(shù).所有的操作會對應(yīng)到當(dāng)?shù)絠MouseDown的狀態(tài)中,只有當(dāng)mouse左鍵為按下時iMouseDown才為真,否則為假. 3、我們創(chuàng)建了全局變量DragDrops與全局函數(shù)CreateDragContainer.DragDrops包含了一系列相對彼此的容器.任何參數(shù)(containers)將通過CreatedcragContainer進行重組與序列化,這樣可以自由的移動.CreateDragContainer函數(shù)也將item進行綁定與設(shè)置屬性. 4、現(xiàn)在我們的代碼知道每個item的加入,當(dāng)我們移動處mouseMove,mouseMove函數(shù)首先會設(shè)置變量target,鼠標(biāo)移動在上面的item,如果這個item在容器中(checked with getAttribute): 運行一小段代碼來改變目標(biāo)的樣式.創(chuàng)造rollover效果 檢查鼠標(biāo)是否沒有放開,如果沒有 設(shè)置curTarget代表當(dāng)前item 記錄item的當(dāng)前位置,如果需要的話,我們可以將它返回 克隆當(dāng)前的item到dragHelper中,我們可以移動帶陰影效果的item. item拷貝到dragHelper后,原有的item還在鼠標(biāo)指針下,我們必須刪除掉dragObj,這樣腳本起作用,dragObj被包含在一個容器中. 抓取容器中所有的item當(dāng)前坐標(biāo),高度/寬度,這樣只需要記錄一次,當(dāng)item被drag時,每隨mouse移動,每移鐘就會記錄成千上萬次. 如果沒有,不需要做任何事,因為這不是一個需要移動的item 5、檢查curTarget,它應(yīng)該包含一個被移動的item,如果存在,進行下面操作: 開始移動帶有陰影的item,這個item就是前文所創(chuàng)建的 檢查每個當(dāng)前容器中的container,是否鼠標(biāo)已經(jīng)移動到這些范圍內(nèi)了 我們檢查看一下正在拖動的item是屬于哪個container 放置item在一個container的某一個item之前,或者整個container之后確認(rèn)item是可見的 如果鼠標(biāo)不在container中,確認(rèn)item是不可見了. 6、剩下的事就是捕捉mouseUp的事件了 該文章在 2010/11/28 9:25:08 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |