文章将深入研究原生JavaScript如何实现拖拽功能,探讨技术细节和最佳实践,为前端开发者提供深刻理解和实际应用的知识。
引言
介绍拖拽功能在现代Web应用中的广泛应用,从图形操作到用户交互体验的提升。
引入原生JavaScript实现拖拽的动机。
HTML结构
分析拖拽功能所需HTML结构的特点。
示例:一个简单的任务列表,演示如何通过拖拽实现任务排序。
基本事件
解析原生JavaScript中拖拽相关的基本事件:ondragstart、ondragenter、ondragend、ondragover。或者mousedown、mousemove、mouseup。
如何监听并响应这些事件。
初始结构样式
html结构
<div class="drag-box">
<div class="drag-item" draggable="true">1</div>
<div class="drag-item" draggable="true">2</div>
<div class="drag-item" draggable="true">3</div>
<div class="drag-item" draggable="true">4</div>
<div class="drag-item" draggable="true">5</div>
<div class="drag-item" draggable="true">6</div>
<div class="drag-item" draggable="true">7</div>
<div class="drag-item" draggable="true">8</div>
</div>
style结构
<style>
.drag-item {
width: 500px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
background: #913291;
cursor: move;
margin-bottom: 10px;
color: #fff;
border: 1px dashed #913291;
}
// 用于拖拽时给的效果,可根据项目所需定制
.active {
background: transparent;
border: 1px dashed #999;
}
</style>
拖拽的基本实现
通过基本事件实现最简单的拖拽功能。
讨论鼠标位置和元素位置之间的关系,实现元素随鼠标移动。
element.ondragstart = (e) => {
console.log('拖动开始')
}
element.ondragenter = (e) => {
console.log('拖动元素进入目标元素时触发')
}
element.ondragend = (e) => {
console.log('当被鼠标拖动的对象进入其容器范围内时触发此事件')
}
element.ondragover = (e) => {
console.log('拖动元素在目标元素上方时触发,通常用于防止默认的拖放行为')
}
功能拆分
定义变量
// 获取列表容器
const element = document.querySelector('.drag-box');
// 当前元素
let currentEl;
移动元素开始时,添加移动时定制的特效或影子
// 这里可通过事件委托监听“拖动开始”事件
element.ondragstart = (e) => {
currentEl = e.target;
// 这里的定时器解决拖动元素时也会带上特效或影子的问题
setTimeout(()=> {
e.target.classList.add('active')
}, 0)
}
处理拖拽时的边界情况,防止元素溢出容器。
// 防止元素溢出容器或拖动到当前元素
element.ondragenter = (e) => {
if(e.target === currentEl || e.target === element) return;
}
拖动完成后移除class效果
//
element.ondragend = (e) => {
e.target.classList.remove('active')
}
近一步对处理拖拽做业务处理
const list = [...element.children];
// 自身位置
const currentIndex = list.indexOf(currentEl);
// 目标位置
const targetIndex = list.indexOf(e.target);
if(currentIndex < targetIndex) {
// 向下拖拽
element.insertBefore(currentEl, e.target.nextElementSibling);
} else {
// 向上拖拽
element.insertBefore(currentEl, e.target)
}
解决只有松开鼠标才是真实变换位置,使用e.preventDefault()阻止拖拽元素的默认行为
element.ondragover = (e) => {
e.preventDefault();
}
最终完整代码
好了,来看看最终的代码效果,首要目的先理解并消化上面的拆分功能代码
// 获取列表容器
const element = document.querySelector('.drag-box');
// 当前元素
let currentEl;
element.ondragstart = (e) => {
currentEl = e.target;
setTimeout(()=> {
e.target.classList.add('active')
}, 0)
}
element.ondragenter = (e) => {
if(e.target === currentEl || e.target === element) return;
const list = [...element.children];
// 自身位置
const currentIndex = list.indexOf(currentEl);
// 目标位置
const targetIndex = list.indexOf(e.target);
if(currentIndex < targetIndex) {
// 向下拖拽
element.insertBefore(currentEl, e.target.nextElementSibling);
} else {
// 向上拖拽
element.insertBefore(currentEl, e.target)
}
}
element.ondragend = (e) => {
e.target.classList.remove('active')
}
element.ondragover = (e) => {
e.preventDefault();
}
分析不同浏览器对原生拖拽的兼容性问题。
提供解决方案,确保在各种环境中都能正常运行