原生JS實(shí)現(xiàn)拖拽排序的示例代碼
說(shuō)到拖拽,應(yīng)用場(chǎng)景不可謂不多。無(wú)論是打開(kāi)電腦還是手機(jī),第一眼望去的界面都是可拖拽的,靠拖拽實(shí)現(xiàn)APP或者應(yīng)用的重新布局,或者拖拽文件進(jìn)行操作文件。
先看效果圖,如何實(shí)現(xiàn)一個(gè)如圖HTML元素的拖拽并排序
HTML中的拖拽事件(drag & drop)
參考MDN中文文檔
事件類(lèi)型
- drag : 當(dāng)拖拽的元素或者選中的文本時(shí)觸發(fā)
- dragend : 當(dāng)拖拽元素結(jié)束時(shí)觸發(fā)
- dragenter : 當(dāng)拖拽元素或選中的文本到一個(gè)可釋放目標(biāo)時(shí)觸發(fā)
- dragleave : 當(dāng)拖拽元素或選中的文本離開(kāi)一個(gè)可釋放目標(biāo)時(shí)觸發(fā)
- dragover : 當(dāng)元素或選中的文本被拖到一個(gè)可釋放目標(biāo)上時(shí)觸發(fā)(每 100 毫秒觸發(fā)一次)
- dragstart : 當(dāng)用戶開(kāi)始拖拽一個(gè)元素或選中的文本時(shí)觸發(fā)
- drop : 當(dāng)元素或選中的文本在可釋放目標(biāo)上被釋放時(shí)觸發(fā)
Coding
寫(xiě)一段簡(jiǎn)單的CSS和html ,實(shí)現(xiàn)初始的頁(yè)面
*{ margin: 0; padding: 0; box-sizing: border-box; } ul{ margin: 200px auto; width: 200px; list-style-type: none; } li{ margin: 5px; text-align: center; width: 200px; height: 30px; background: skyblue; } .list .moving{ background: transparent; color: transparent; border: 1px dashed #ccc; }
<ul class="list"> <li >1</li> <li >2</li> <li >3</li> <li >4</li> <li >5</li> </ul>
此時(shí)我們的頁(yè)面如下圖
現(xiàn)在還不可以進(jìn)行拖拽操作,為了可以實(shí)現(xiàn)拖拽操作,我們必須給每個(gè)元素設(shè)置 draggable="true"
<ul class="list"> <li draggable="true">1</li> <li draggable="true">2</li> <li draggable="true">3</li> <li draggable="true">4</li> <li draggable="true">5</li> </ul>
元素已經(jīng)可以基礎(chǔ)的拖拽
接下來(lái)我們需要在JS中對(duì)DOM元素進(jìn)行一系列操作來(lái)實(shí)現(xiàn)對(duì)應(yīng)的效果
- 實(shí)現(xiàn)拖出去的元素,原位置樣式變?yōu)橥该魈摼€
- 實(shí)現(xiàn)拖動(dòng)到其他元素上時(shí),列表順序發(fā)生改變
let list = document.querySelector('.list') let currentLi // 記錄拖拽元素
我們用事件委托,監(jiān)聽(tīng) "dragstart" 事件,給拖動(dòng)的元素添加類(lèi)名,修改樣式,這里會(huì)出現(xiàn)奇怪的一幕就是,拖動(dòng)的樣式和原來(lái)的樣式同時(shí)變成了透明。
list.addEventListener('dragstart',(e)=>{ e.dataTransfer.effectAllowed = 'move' // 拖動(dòng)樣式改為 "move" currentLi = e.target currentLi.classList.add('moving') })
這里會(huì)出現(xiàn)奇怪的一幕就是,拖動(dòng)的樣式和原來(lái)的樣式同時(shí)變成了透明。這是因?yàn)楦S鼠標(biāo)拖動(dòng)的元素的樣式在拖動(dòng)的那一刻是原始元素的樣式,所以也會(huì)添加"moving", 那么在這里我們加一個(gè)異步
list.addEventListener('dragstart',(e)=>{ e.dataTransfer.effectAllowed = 'move' currentLi = e.target setTimeout(()=>{ currentLi.classList.add('moving') }) })
到這里距離目標(biāo)又更近了一步,
接下來(lái)我們需要在拖動(dòng)的過(guò)程中對(duì)列表的元素進(jìn)行重新的排序
Node.insertBefore():方法在參考節(jié)點(diǎn)之前插入一個(gè)擁有指定父節(jié)點(diǎn)的子節(jié)點(diǎn)
list.addEventListener('dragenter',(e)=>{ e.preventDefault() // 阻止默認(rèn)事件 if(e.target === currentLi||e.target === list){ // 當(dāng)移動(dòng)到當(dāng)前拖動(dòng)元素,或者父元素上面我們不做操作 return } let liArray = Array.from(list.childNodes) let currentIndex = liArray.indexOf(currentLi) // 獲取到拖動(dòng)元素的下標(biāo) let targetindex = liArray.indexOf(e.target) // 獲取到目標(biāo)元素的下標(biāo) if(currentIndex<targetindex){ list.insertBefore(currentLi,e.target.nextElementSibling) }else{ list.insertBefore(currentLi,e.target) } })
最后我們需要在拖拽結(jié)束將元素的moving類(lèi)名移除,以及阻止拖拽到一個(gè)目標(biāo)上的默認(rèn)事件(否則會(huì)出現(xiàn)禁止)
list.addEventListener('dragover',(e)=>{ e.preventDefault() }) list.addEventListener('dragend',(e)=>{ currentLi.classList.remove('moving') })
至此,一個(gè)簡(jiǎn)單的拖拽排序功能就實(shí)現(xiàn)了
完整代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> *{ margin: 0; padding: 0; box-sizing: border-box; } ul{ margin: 200px auto; width: 200px; list-style-type: none; } li{ margin: 5px; text-align: center; width: 200px; height: 30px; background: skyblue; } .list .moving{ background: transparent; color: transparent; border: 1px dashed #ccc; } </style> </head> <body> <ul class="list"> <li draggable="true">1</li> <li draggable="true">2</li> <li draggable="true">3</li> <li draggable="true">4</li> <li draggable="true">5</li> </ul> <script> let list = document.querySelector('.list') let currentLi list.addEventListener('dragstart',(e)=>{ e.dataTransfer.effectAllowed = 'move' currentLi = e.target setTimeout(()=>{ currentLi.classList.add('moving') }) }) list.addEventListener('dragenter',(e)=>{ e.preventDefault() if(e.target === currentLi||e.target === list){ return } let liArray = Array.from(list.childNodes) let currentIndex = liArray.indexOf(currentLi) let targetindex = liArray.indexOf(e.target) if(currentIndex<targetindex){ list.insertBefore(currentLi,e.target.nextElementSibling) }else{ list.insertBefore(currentLi,e.target) } }) list.addEventListener('dragover',(e)=>{ e.preventDefault() }) list.addEventListener('dragend',(e)=>{ currentLi.classList.remove('moving') }) </script> </body> </html>
以上就是原生JS實(shí)現(xiàn)拖拽排序的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于JS拖拽排序的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JS與SQL方式隨機(jī)生成高強(qiáng)度密碼示例
這篇文章主要介紹了JS與SQL方式隨機(jī)生成高強(qiáng)度密碼,結(jié)合實(shí)例形式分析了javascript方式與SQL方式生成高強(qiáng)度密碼的相關(guān)操作技巧,需要的朋友可以參考下2018-12-12自己做的模擬模態(tài)對(duì)話框?qū)崿F(xiàn)代碼
最近做完一個(gè)項(xiàng)目,發(fā)現(xiàn)瀏覽器兼容問(wèn)題,模態(tài)對(duì)話框只有IE支持,但是IE9又不能支持帶框架的對(duì)話框,那個(gè)對(duì)話框的大小打死都設(shè)置不了,在網(wǎng)上查說(shuō)因?yàn)楹枚喙δ鼙籌E9屏蔽了,于是自己做了一個(gè)模擬對(duì)話框的東西2012-05-05一文詳解前端進(jìn)階之IntersectionObserver
這篇文章主要為大家介紹了前端進(jìn)階之IntersectionObserver示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04基于layui數(shù)據(jù)表格以及傳數(shù)據(jù)的方式
今天小編就為大家分享一篇基于layui數(shù)據(jù)表格以及傳數(shù)據(jù)的方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08JS 控制小數(shù)位數(shù)的實(shí)現(xiàn)代碼
上網(wǎng)查一查的確存在這種Bug,除了位數(shù)上控制之外也沒(méi)什么也好的方法(希望高手能提出其它思路)。2011-08-08javascript作用域容易記錯(cuò)的兩個(gè)地方分析
javascript作用域容易記錯(cuò)的兩個(gè)地方分析,學(xué)習(xí)js的朋友可以參考下2012-06-06動(dòng)態(tài)加載iframe時(shí)get請(qǐng)求傳遞中文參數(shù)亂碼解決方法
這篇文章主要介紹了動(dòng)態(tài)加載iframe時(shí)get請(qǐng)求傳遞中文參數(shù)亂碼解決方法,需要的朋友可以參考下2014-05-05chorme 瀏覽器記住密碼后input黃色背景處理方法(兩種)
使用chrome瀏覽器選擇記住密碼的賬號(hào),輸入框會(huì)自動(dòng)加上黃色的背景,有些設(shè)計(jì)輸入框是透明背景的,需要去除掉這個(gè)黃色的背景。下面給大家分享chorme 瀏覽器記住密碼后input黃色背景處理方法,一起看看吧2017-11-11