前言
列表拖拽排序是一个常见的需求,实现的方式很多,接下来让我们使用HTML5的一个新特性draggable属性实现这个功能。
Main
需求思路:
- 获取所有
li
元素。 - 创建变量
draggedItem
用于存储被拖拽的元素。 - 遍历所有
li
元素,给每个元素添加三个事件监听器:dragstart
事件:当元素开始被拖拽时,将该元素存储到draggedItem
中,并将该元素隐藏。dragover
事件:当元素被拖拽经过另一个元素时,阻止默认行为,判断该元素是否为合法的拖放目标(不是被拖拽的元素本身,且为li
元素),计算目标元素的位置,根据拖拽的位置插入被拖拽的元素到目标元素的前面或后面。dragend
事件:当元素拖拽结束时,将元素显示出来,并将draggedItem
设为null
。
Tip:当我们得到一个需求的时候,可以先列出编码的思路,然后再去code,就会思路清晰哝!
然后就要开始编码了哝。
<ul class="sortable-list">
<li draggable="true" id="item1">Item 1</li>
<li draggable="true" id="item2">Item 2</li>
<li draggable="true" id="item3">Item 3</li>
<li draggable="true" id="item4">Item 4</li>
<li draggable="true" id="item5">Item 5</li>
<li draggable="true" id="item6">Item 6</li>
<li draggable="true" id="item7">Item 7</li>
<li draggable="true" id="item8">Item 8</li>
<li draggable="true" id="item9">Item 9</li>
<li draggable="true" id="item10">Item 10</li>
<li draggable="true" id="item11">Item 11</li>
<li draggable="true" id="item12">Item 12</li>
</ul>
CSS 部分定义了可拖动列表项的样式:
.sortable-list {
list-style: none;
/* 去除列表默认的样式 */
margin: 0;
/* 去除默认的外边距 */
padding: 0;
/* 去除默认的内边距 */
}
li {
background-color: #f2f2f2;
/* 设置背景颜色 */
border: 1px solid #ccc;
/* 设置边框 */
cursor: move;
/* 设置鼠标样式为移动 */
margin-bottom: 5px;
/* 设置下边距 */
padding: 10px;
/* 设置内边距 */
transition: all 0.2s ease-in-out;
/* 设置过渡动画效果 */
}
li:hover {
background-color: #e0e0e0;
/* 当鼠标悬停在元素上时,改变背景颜色 */
}
li:active {
background-color: #ccc;
/* 当元素被按下时,改变背景颜色 */
}
li.placeholder {
background-color: #ddd;
/* 占位符元素的背景颜色 */
opacity: 0.5;
/* 占位符元素的透明度 */
}
JavaScript部分定义拖拽排序逻辑:
// 选择所有 li 元素
const items = document.querySelectorAll('li');
// 创建一个变量来存储被拖动的元素
let draggedItem = null;
// 遍历每个 li 元素并为其添加事件监听器
for (let i = 0; i < items.length; i++) {
const item = items[i];
// 当拖动开始时触发
item.addEventListener('dragstart', function () {
// 将当前被拖动的元素赋值给变量
draggedItem = item;
// 使用 setTimeout 方法来确保当前被拖动的元素在开始拖动时被隐藏
setTimeout(function () {
item.style.display = 'none';
}, 0);
});
// 当拖动元素在 li 元素上方时触发
item.addEventListener('dragover', function (e) {
e.preventDefault();
// 确保目标元素不是被拖动的元素本身,并且目标元素是 li 元素
if (e.target !== draggedItem && e.target.nodeName === 'LI') {
// 获取目标元素的位置信息
const bounding = e.target.getBoundingClientRect();
// 计算目标元素的中心点位置
const offset = bounding.y + bounding.height / 2;
// 如果当前鼠标位置在目标元素的上半部分,则将被拖动的元素插入到目标元素之前
if (e.clientY < offset) {
e.target.parentNode.insertBefore(draggedItem, e.target);
// 如果当前鼠标位置在目标元素的下半部分,则将被拖动的元素插入到目标元素之后
} else {
e.target.parentNode.insertBefore(draggedItem, e.target.nextSibling);
}
}
});
// 当拖动结束时触发
item.addEventListener('dragend', function () {
// 将被拖动的元素重新显示出来
draggedItem.style.display = 'block';
// 清除对被拖动元素的引用
draggedItem = null;
});
}
效果展示 gif效果不太好
结束语:如果对您有帮助!点个赞,加个关注吧,有问题评论区留言交流哦!