拖放事件:
拖动元素时,依次触发的事件
- dragstart 按下鼠标并移动时触发
- drag 拖动期间持续触发
- dragend 松开鼠标触发
拖动到有效的放置目标上时,依次触发
- dragenter 元素被拖到放置目标上
- dragover 被拖元素在放置目标内移动,持续触发
- dragleave 元素被拖到放置目标外触发
- drop 元素被拖到有效的放置目标上(默认无效),并松开鼠标触发
实现代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>table</title>
<style>
tr,th {
padding: 30px;
}
</style>
</head>
<body>
<table id="myTable" border="1" >
<thead>
<tr>
<th>序列</th>
<th>名称</th>
<th>单价</th>
<th>数量</th>
</tr>
</thead>
<tbody>
<tr draggable="true">
<td>1</td>
<td>冰箱</td>
<td>13</td>
<td>14</td>
</tr>
<tr draggable="true">
<td>2</td>
<td>彩电</td>
<td>113</td>
<td>114</td>
</tr>
<tr draggable="true">
<td>3</td>
<td>空调</td>
<td>12</td>
<td>12</td>
</tr>
<tr draggable="true">
<td>4</td>
<td>洗衣机</td>
<td>12</td>
<td>12</td>
</tr>
</tbody>
</table>
<script>
function drag_row_table() {
var EventUtil = { //跨浏览器的事件处理程序,视情况分别选择以下事件处理程序
addHandler: function(element, type, handler) {
if(element.addEventListener) { //DOM2级
element.addEventListener(type, handler, false);
}
else if (element.attachEvent) { //IE方法
element.attachEvent('on' + type, handler);
}
else { //DOM1级
element["on" + type] = handler;
}
},
getTarget: function(event) { //获取事件的实际目标(在这里是tr),不同于event.currentTarget和this指向的是绑定事件处理程序的目标,(tbody)
return event.target || event.srcElement;
},
preventDefault: function(event) { //取消事件默认方法
if(event.preventDefault) {
event.preventDefault();
}
else {
event.returnValue = false; //默认为true,设为falsew为取消默认行为。
}
}
}
var myTable = document.querySelector('#myTable');
var tbody = myTable.querySelector("tbody"); //这里监听的是tbody,而不是tr。目的是利用事件冒泡,减少监听的元素个数,提高性能
var trs = tbody.querySelectorAll("tr");
EventUtil.addHandler(tbody, "dragover", function(event) { //默认元素不可放置,这里取消默认,将放置目标修改为可放置的
EventUtil.preventDefault(event);
})
EventUtil.addHandler(tbody, "dragstart", function(event) { //监听点击的拖动的元素
event.dataTransfer.setData("drag_index", event.target.rowIndex); //将被拖元素的信息,传给放置位置
})
EventUtil.addHandler(tbody, "drop", function(event) { //监听鼠标移动到可放置的放置目标上,松开鼠标的事件
EventUtil.preventDefault(event); //取消在Firefox 3.5+中,放置事件的默认行为是打开被放置目标上的URL
let drag_index = parseInt(event.dataTransfer.getData("drag_index")); //获取传过来的被拖元素的信息
let drop_index = EventUtil.getTarget(event).parentNode.rowIndex; //this为当前触发drop的元素,即放置目标的行下标
tbody.insertBefore(trs[drag_index], EventUtil.getTarget(event).parentNode.nextSibling); //将被拖元素放到放置目标的兄弟节点上
trs = myTable.querySelectorAll("tr"); //更新变换的tr行信息
})
}
drag_row_table();
</script>
</body>
</html>
效果:
这里遇到的坑是,querySelectorAll()方法的底层实现是类似于一组元素的快照,而不是动态查询的(即更新文档结构时,对应的变量也自动更新),为的是避免使用NodeList对象引起的性能问题。因此我们得手动更新querySelectorAll()方法的变量。
符合动态查询的方法有getElementsByTagName()