利用HTML5新特性实现拖拽交换表格单元格元素
HTML5新特性:拖放
拖放
拖放(Drag 和 Drop)是很常见的特性。它指的是您抓取某物并拖入不同的位置。
拖放是 HTML5 标准的组成部分:任何元素都是可拖放的。
浏览器支持
表格中的数字指示了完全支持拖放的首个浏览器版本。
HTML 拖放实例
下列是关于拖放的简单例子:
实例
<!DOCTYPE HTML>
<html>
<head>
<script> function allowDrop(ev) { ev.preventDefault(); } function drag(ev) { ev.dataTransfer.setData("text", ev.target.id); } function drop(ev) { ev.preventDefault(); var data = ev.dataTransfer.getData("text"); ev.target.appendChild(document.getElementById(data)); } </script>
</head>
<body>
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<img id="drag1" src="img_logo.gif" draggable="true" ondragstart="drag(event)" width="336" height="69">
</body>
</html>
实例网址:W3School TIY Editor
拖放事件详解
把元素设置为可拖放
首先,为了把一个元素设置为可拖放,请把它的 draggable 属性设置为 true:
<img draggable="true">
设置拖放的内容 – ondragstart 和 setData()
然后,规定当元素被拖动时发生的事情。
在上面的例子中,ondragstart 属性调用了一个 drag(event) 函数,规定拖动什么数据。
dataTransfer.setData() 方法设置被拖动数据的数据类型和值:
function drag(ev) { ev.dataTransfer.setData("text", ev.target.id); }
在本例中,数据类型是 “text”,而值是这个可拖动元素的 id (“drag1”)。
设置拖到何处 – ondragover
ondragover 事件规定被拖动的数据能够被放置到何处。
默认地,数据/元素无法被放置到其他元素中。为了实现拖放,我们必须阻止元素的这种默认的处理方式。
这个任务由 ondragover 事件的 event.preventDefault() 方法完成:
function allowDrop(ev) { ev.preventDefault(); }
进行放置 – ondrop
当放开被拖数据时,会发生 drop 事件。
在上面的例子中,ondrop 属性调用了一个函数,drop(event):
function drop(ev) { ev.preventDefault(); var data = ev.dataTransfer.getData("text"); ev.target.appendChild(document.getElementById(data)); }
代码解释:
调用 preventDefault() 来阻止数据的浏览器默认处理方式(drop 事件的默认行为是以链接形式打开)
通过 dataTransfer.getData() 方法获得被拖的数据。该方法将返回在 setData() 方法中设置为相同类型的任何数据
被拖数据是被拖元素的 id (“drag1”)
把被拖元素追加到放置元素中
项目实战运用
<table style="table-layout: fixed; line-height: 45px; font-size: 16px;" width="100%" border="1" cellpadding="4" cellspacing="0">
<tr style="background-color: rgb(132 188 247); color: rgb(255, 255, 255); text-align: center;">
<th>时间段</th>
<th>星期一</th>
<th>星期二</th>
<th>星期三</th>
<th>星期四</th>
<th>星期五</th>
<th>星期六</th>
<th>星期日</th>
</tr>
<tr style="text-align: center;">
<td>时间段</td>
<td>……</td>
<!-- 已省略不重要的部分,主要实现以下两个单元格的拖拽交换 -->
<td>
<div @drop="drop($event)" @dragover="allowDrop($event)" draggable="true" @dragstart="drag($event)" :id="test1.saturday.id">
{
{test1.saturday.user}}
</div>
</td>
<td>
<div @drop="drop($event)" @dragover="allowDrop($event)" draggable="true" @dragstart="drag($event)" :id="test1.sunday.id">
{
{test1.sunday.user}}
</div>
</td>
</tr>
</table>
// 实现拖拽交换单元格内元素
function allowDrop(ev: any) {
ev.preventDefault();
}
// 拖动时触发
function drag(ev: any) {
ev.dataTransfer.setData("id", ev.target.id);
ev.target.style.border = "1px solid yellow";
ev.target.style.cursor = "pointer";
}
// 放下后触发
function drop(ev: any) {
ev.preventDefault();
var data = ev.dataTransfer.getData("id");
swapElements(document.getElementById(data), ev.currentTarget);
ev.target.style.border = "1px solid yellow";
ev.target.style.cursor = "pointer";
// 发送交换请求
axios.post('/apply/manager/arrange', {
"method": "exchange",
"shiftFirstId": data,
"shiftSecondId": ev.target.id,
}).then((res: any)=>{
if (res.code === 200) {
message.success(res.msg);
} else {
message.error(res.msg);
}
})
}
// 交换元素
function swapElements(a: any, b: any) {
// 交换两个dom元素
if (a == b) return;
//记录父元素
var bp = b.parentNode;
var ap = a.parentNode;
//记录下一个同级元素
var an = a.nextElementSibling;
var bn = b.nextElementSibling;
//如果参照物是邻近元素则直接调整位置
if (an == b) return bp.insertBefore(b, a);
if (bn == a) return ap.insertBefore(a, b);
if (a.contains(b)) {
//如果a包含了b
return ap.insertBefore(b, a), bp.insertBefore(a, bn);
} else {
return bp.insertBefore(a, b), ap.insertBefore(b, an);
}
}
踩坑记录
- 因为需求要做到两个元素的拖拽交换,所以这两个元素都要设置draggable为true并绑定ondrop, ondragstart, ondragover事件。
- 在drop方法中,不能简单的用ev.target.appendChild(document.getElementById(data))来实现,如果用这种方法实际上是将元素1的内容追加到元素2的dom中,无法达到交换的效果。
- 也不能用ev.target.replaceChild(document.getElementById(data), ev.target.firstChild)来做互换,会出现bug。