HTML5实现了原生拖动的接口,因此在实现拖拽功能时,可以直接使用该方法。下面用一个例子学习其用法。
案例背景:
实现ol列表的拖拽可排序功能。
实现过程:
拟实现一个类似于下面的ol列表。该列表需要有可拖拽调整顺序的功能。给标签加入draggable属性,标签即赋予拖拽功能。li标签在JS中用数据动态写入,方便拖拽时对数据进行操作,从而控制列表顺序。
li相应的css代码如下
li{
border-bottom:1px solid #ccc;
width:100px;
margin-bottom:5px;
}
JS动态写入标签
JS中加入以下方法生成li标签,给每个li标签加入draggable属性使其可拖拽,以及order属性标记索引值。
let data = ['苹果', '香蕉', '梨','草莓']
initDom=(data)=>{
let ol = document.getElementsByTagName('ol')[0]
ol.innerHTML=""
data.map((item,index)=>{
let newElement = document.createElement('li')
newElement.setAttribute('draggable', true)
newElement.setAttribute('order', index)
let textNode = document.createTextNode(item)
newElement.appendChild(textNode)
ol.appendChild(newElement)
})
}
initDom(data)
拖动事件处理:
拖动事件主要由以下几个事件组成:
dragstart: 开始拖拽的时候触发的事件,作用于被拖拽的元素。
dragenter: 拖拽元素进入目标元素时触发,作用于目标元素。
dragover:拖拽元素在目标元素上方移动时触发,作用于目标元素。
dragleave: 拖拽元素离开目标元素时触发,作用于目标元素。
drop: 鼠标放开拖拽元素时触发在目标元素上的事件。
dragend: 拖拽结束后,作用于被拖拽元素上的事件。
上述例子中,利用事件冒泡在外层ol标签上加入拖拽事件,不用加在每个li标签上。
dragstart:
在开始拖拽时,希望被拖拽元素加入透明度的样式以进行标记。dataTransfer可以在拖拽过程中传递数据。此处将索引值传递过去,方便最后进行重排序。
let ol = document.getElementsByTagName('ol')[0];
ol.οndragstart=(event)=>{
event.target.style.opacity = 0.4;
const dragIndex = event.target.attributes.order.nodeValue
event.dataTransfer.setData("text/dragIndex",dragIndex);
}
dragover:
在拖动过程中,如果在目标元素上方移动时,给目标元素加一个样式进行标记。
ol.ondragover = (event)=>{
//默认拒绝接受任何被拖放的元素,取消默认
event.preventDefault();
if(event.target.nodeName.toLowerCase()==='li'){
event.target.style.borderBottom = '1px solid red'
}
}
dragleave:
当被拖动元素离开目标元素时,需要恢复目标元素的样式。
ol.ondragleave = (event)=>{
if(event.target.nodeName.toLowerCase()==='li'){
event.target.style.borderBottom = '1px solid #ccc'
}
}
dragend:
当拖动结束时,恢复被拖拽元素的样式。
ol.ondragend = (event)=>{
event.target.style.opacity = 1;
}
dragdrop:
放下拖拽元素时,需要处理数据逻辑,以更改DOM结构,交换元素。
ol.ondrop = (event)=>{
//浏览器默认元素会阻止drop,所以取消默认
event.preventDefault();
if(event.target.nodeName.toLowerCase()==='li'){
const dropIndex = Number(event.target.attributes.order.nodeValue)
const dragIndex = Number(event.dataTransfer.getData("text/dragIndex"))
const dragData = data[dragIndex]
const dropData = data[dropIndex]
data.splice(dropIndex, 1, dragData)
data.splice(dragIndex, 1, dropData)
initDom(data)
}
}
注意点:
dragover和drop事件需要加入event.preventDefault(),否则该事件会不起作用。
拖拽过程中数据的传递可通过event.dataTransfer.setData("数据类型名称",数据)和event.dataTransfer.getData("数据类型名称")实现。
拖拽结束时的数据交换逻辑需要写在drop中而不是dragend,dragend作用的元素是被拖拽的元素。