javascript - 使用 vanila JS 使表格行可拖动
问题描述
我有一个像这样的简单表:
╔═════════════╦═════════╦═════════╗
║ Col1 ║ Col2 ║ Col3 ║
╠═════════════╬═════════╬═════════╣
║ Value 1 ║ Value 2 ║ Value 3 ║
║ Value 4 ║ Value 5 ║ Value 6 ║
║ Value 7 ║ Value 8 ║ Value 9 ║
╚═════════════╩═════════╩═════════╝
我需要它,因此具有值的行是可拖动的,因此我可以更改它们的顺序。类似的方式.sortable()
在 jquery 中有效,但我需要它在 vanila JS 中。我找到了许多使用不同库的解决方案,但在普通 JS 中没有。
有足够简单的解决方案吗?我不需要动画或库仅提供功能的任何东西。
提前感谢您提供任何形式的帮助。
解决方案
在 codepen 上找到了一个使用 vanila JS 的工作解决方案。
https://codepen.io/chingy/pen/Exxvpjo?editors=1010
JS
(function() {
"use strict";
const table = document.getElementById('table');
const tbody = table.querySelector('tbody');
var currRow = null,
dragElem = null,
mouseDownX = 0,
mouseDownY = 0,
mouseX = 0,
mouseY = 0,
mouseDrag = false;
function init() {
bindMouse();
}
function bindMouse() {
document.addEventListener('mousedown', (event) => {
if(event.button != 0) return true;
let target = getTargetRow(event.target);
if(target) {
currRow = target;
addDraggableRow(target);
currRow.classList.add('is-dragging');
let coords = getMouseCoords(event);
mouseDownX = coords.x;
mouseDownY = coords.y;
mouseDrag = true;
}
});
document.addEventListener('mousemove', (event) => {
if(!mouseDrag) return;
let coords = getMouseCoords(event);
mouseX = coords.x - mouseDownX;
mouseY = coords.y - mouseDownY;
moveRow(mouseX, mouseY);
});
document.addEventListener('mouseup', (event) => {
if(!mouseDrag) return;
currRow.classList.remove('is-dragging');
table.removeChild(dragElem);
dragElem = null;
mouseDrag = false;
});
}
function swapRow(row, index) {
let currIndex = Array.from(tbody.children).indexOf(currRow),
row1 = currIndex > index ? currRow : row,
row2 = currIndex > index ? row : currRow;
tbody.insertBefore(row1, row2);
}
function moveRow(x, y) {
dragElem.style.transform = "translate3d(" + x + "px, " + y + "px, 0)";
let dPos = dragElem.getBoundingClientRect(),
currStartY = dPos.y, currEndY = currStartY + dPos.height,
rows = getRows();
for(var i = 0; i < rows.length; i++) {
let rowElem = rows[i],
rowSize = rowElem.getBoundingClientRect(),
rowStartY = rowSize.y, rowEndY = rowStartY + rowSize.height;
if(currRow !== rowElem && isIntersecting(currStartY, currEndY, rowStartY, rowEndY)) {
if(Math.abs(currStartY - rowStartY) < rowSize.height / 2)
swapRow(rowElem, i);
}
}
}
function addDraggableRow(target) {
dragElem = target.cloneNode(true);
dragElem.classList.add('draggable-table__drag');
dragElem.style.height = getStyle(target, 'height');
dragElem.style.background = getStyle(target, 'backgroundColor');
for(var i = 0; i < target.children.length; i++) {
let oldTD = target.children[i],
newTD = dragElem.children[i];
newTD.style.width = getStyle(oldTD, 'width');
newTD.style.height = getStyle(oldTD, 'height');
newTD.style.padding = getStyle(oldTD, 'padding');
newTD.style.margin = getStyle(oldTD, 'margin');
}
table.appendChild(dragElem);
let tPos = target.getBoundingClientRect(),
dPos = dragElem.getBoundingClientRect();
dragElem.style.bottom = ((dPos.y - tPos.y) - tPos.height) + "px";
dragElem.style.left = "-1px";
document.dispatchEvent(new MouseEvent('mousemove',
{ view: window, cancelable: true, bubbles: true }
));
}
function getRows() {
return table.querySelectorAll('tbody tr');
}
function getTargetRow(target) {
let elemName = target.tagName.toLowerCase();
if(elemName == 'tr') return target;
if(elemName == 'td') return target.closest('tr');
}
function getMouseCoords(event) {
return {
x: event.clientX,
y: event.clientY
};
}
function getStyle(target, styleName) {
let compStyle = getComputedStyle(target),
style = compStyle[styleName];
return style ? style : null;
}
function isIntersecting(min0, max0, min1, max1) {
return Math.max(min0, max0) >= Math.min(min1, max1) &&
Math.min(min0, max0) <= Math.max(min1, max1);
}
init();
})();
CSS:
.draggable-table {
position: absolute;
top: 25%;
left: 20%;
width: 60%;
height: 50%;
border-collapse: collapse;
background: white;
-webkit-box-shadow: 0px 0px 10px 8px rgba(0, 0, 0, 0.1);
box-shadow: 0px 0px 10px 8px rgba(0, 0, 0, 0.1);
}
.draggable-table .draggable-table__drag {
font-size: 0.95em;
font-weight: lighter;
text-transform: capitalize;
position: absolute;
width: 100%;
text-indent: 50px;
border: 1px solid #f1f1f1;
z-index: 10;
cursor: grabbing;
-webkit-box-shadow: 2px 2px 3px 0px rgba(0, 0, 0, 0.05);
box-shadow: 2px 2px 3px 0px rgba(0, 0, 0, 0.05);
opacity: 1;
}
HTML
<table id="table" class="draggable-table">
<thead>
<th>Name</th>
<th>Occupation</th>
</thead>
<tbody>
<tr>
<td>April Douglas</td>
<td>Health Educator</td>
</tr>
<tr>
<td>Salma Mcbride</td>
<td>Mental Health Counselor</td>
</tr>
<tr>
<td>Kassandra Donovan</td>
<td>Makeup Artists</td>
</tr>
<tr>
<td>Yosef Hartman</td>
<td>Theatrical and Performance</td>
</tr>
<tr>
<td>Ronald Mayo</td>
<td>Plant Etiologist</td>
</tr>
<tr>
<td>Trey Woolley</td>
<td>Maxillofacial Surgeon</td>
</tr>
</tbody>
</table>
推荐阅读
- android - NumberFormatException:无效的 int:res/drawable-xhdpi-v4/ic_launcher_background.png
- collections - 在这里,它不是不可变的,因为 HashMap(field value) 的 key & value 可以更改。请提出解决方案以使其成为真正的不可变类
- javascript - 我想使用 javascript 切换 CSS 属性
- python - 在 docker 内部运行 python 测试并在主机外部获取退出代码
- git - 在谷歌云构建中使用 git 容器进行 git commit
- flutter - Flutter:如何检查 DataSnapshot 是否为空?
- android - Flutter-ListView 溢出
- javascript - 在数据表上添加条件多列过滤器
- pyspark - Pyspark ..滞后于生成/计算的列
- rest - 子 api 的包装器 api (ASP.NET Core2)