JavaScript可排序列表跨列表拖拽异常及代码优化咨询
解决方案与代码优化
一、阻止跨列表拖拽的核心修改
问题根源是未校验拖拽源列表与目标列表是否一致,导致跨列表拖拽时错误触发了目标列表的交换逻辑。只需两步修改即可解决:
记录拖拽源列表类型
新增变量存储拖拽元素所属的列表类型,而非仅记录索引:let dragSource; // 存储源列表类型('left'/'right') let dragStartIndex; function dragStart(e) { const parent = this.closest('[data-index]'); dragStartIndex = +parent.dataset.index; // 通过dataset直接获取源列表标识 dragSource = parent.dataset.left ? 'left' : 'right'; }校验源与目标列表匹配性
在dragDrop中判断,仅当源列表和目标列表一致时才执行交换:function dragDrop(e) { const targetParent = this.closest('[data-index]'); const targetList = targetParent.dataset.left ? 'left' : 'right'; // 核心判断:跨列表拖拽直接终止逻辑 if (dragSource !== targetList) return; const dragEndIndex = +targetParent.dataset.index; swapItems(dragStartIndex, dragEndIndex, targetList); }
二、代码优化建议
1. 封装重复的列表创建逻辑
左右列表的生成逻辑高度冗余,封装为通用函数减少重复代码:
function createListContainer(container, items, listArray, dataAttr) { [...items] .map(item => ({ value: item, sort: Math.random() })) .sort((a, b) => a.sort - b.sort) .forEach((item, index) => { const element = document.createElement('div'); element.dataset.index = index; element.dataset[dataAttr] = true; element.innerHTML = ` <div class="draggable ${dataAttr}-draggable" draggable="true" data-${dataAttr}="true"> <p class="item-name">${item.value}</p> </div> `; container.appendChild(element); listArray.push(element); }); } // 调用时简化为: function createList() { createListContainer(leftDiv, pairs.map(p => p.pol), leftList, 'left'); createListContainer(rightDiv, pairs.map(p => p.eng), rightList, 'right'); addEventListener(); }
2. 使用dataset替代getAttribute
dataset是更简洁的自定义属性操作方式,替代繁琐的getAttribute/setAttribute:
- 替换
element.setAttribute('data-left', true)为element.dataset.left = true - 替换
e.target.getAttribute('data-left')为e.target.dataset.left
3. 简化事件监听逻辑
直接在创建元素时绑定事件,避免后续重复查询DOM:
// 在createListContainer函数中,创建元素后直接绑定事件 const draggable = element.querySelector('.draggable'); draggable.addEventListener('dragstart', dragStart); element.addEventListener('dragenter', dragEnter); element.addEventListener('dragleave', dragLeave); element.addEventListener('dragover', dragOver); element.addEventListener('drop', dragDrop); // 可删除单独的addEventListener函数,减少DOM查询开销
4. 优化交换逻辑
利用数组解构交换元素,结合DOM重排简化代码:
function swapItems(fromIndex, toIndex, listType) { const list = listType === 'left' ? leftList : rightList; // 解构交换数组元素 [list[fromIndex], list[toIndex]] = [list[toIndex], list[fromIndex]]; // 重排DOM节点,自动更新顺序 const container = listType === 'left' ? leftDiv : rightDiv; container.innerHTML = ''; list.forEach(item => container.appendChild(item)); // 更新每个元素的data-index list.forEach((item, index) => item.dataset.index = index); }
三、完整优化后代码
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="style.css"> </head> <body> <div class="grid"> <div class="left-side"></div> <div class="right-side"></div> </div> <script src="main.js"></script> </body> </html>
CSS
*{ box-sizing: border-box; margin: 0; padding: 0; } .grid { display: flex; margin: 50px auto; width: 500px; justify-content: center; gap: 30px; } div[data-index] { padding: 25px; border: solid 2px #333; margin-bottom: 10px; } .item-name { font-size: 25px; font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; text-transform: uppercase; padding: 10px 20px; border: 2px solid #ccc; border-radius: 16px; text-align: center; cursor: pointer; } .draggable:active { cursor: grabbing; }
JavaScript
const leftDiv = document.querySelector('.left-side') const rightDiv = document.querySelector('.right-side') const pairs = [ {pol: 'jeden', eng: 'one'}, {pol: 'dwa', eng: 'two'}, {pol: 'trzy', eng: 'three'}, {pol: 'cztery', eng: 'four'}, {pol: 'piec', eng: 'five'}, ] const leftList = []; const rightList = []; let dragSource; let dragStartIndex; createList(); function createListContainer(container, items, listArray, dataAttr) { [...items] .map(item => ({ value: item, sort: Math.random() })) .sort((a, b) => a.sort - b.sort) .forEach((item, index) => { const element = document.createElement('div'); element.dataset.index = index; element.dataset[dataAttr] = true; element.innerHTML = ` <div class="draggable ${dataAttr}-draggable" draggable="true" data-${dataAttr}="true"> <p class="item-name">${item.value}</p> </div> `; // 绑定拖拽事件 const draggable = element.querySelector('.draggable'); draggable.addEventListener('dragstart', dragStart); element.addEventListener('dragenter', dragEnter); element.addEventListener('dragleave', dragLeave); element.addEventListener('dragover', dragOver); element.addEventListener('drop', dragDrop); container.appendChild(element); listArray.push(element); }); } function createList() { createListContainer(leftDiv, pairs.map(p => p.pol), leftList, 'left'); createListContainer(rightDiv, pairs.map(p => p.eng), rightList, 'right'); } function dragStart(e) { const parent = this.closest('[data-index]'); dragStartIndex = +parent.dataset.index; dragSource = parent.dataset.left ? 'left' : 'right'; } function dragEnter() {} function dragLeave() {} function dragOver(e) { e.preventDefault(); } function dragDrop(e) { const targetParent = this.closest('[data-index]'); const targetList = targetParent.dataset.left ? 'left' : 'right'; if (dragSource !== targetList) return; const dragEndIndex = +targetParent.dataset.index; swapItems(dragStartIndex, dragEndIndex, targetList); } function swapItems(fromIndex, toIndex, listType) { const list = listType === 'left' ? leftList : rightList; [list[fromIndex], list[toIndex]] = [list[toIndex], list[fromIndex]]; const container = listType === 'left' ? leftDiv : rightDiv; container.innerHTML = ''; list.forEach(item => container.appendChild(item)); list.forEach((item, index) => { item.dataset.index = index; }); }
内容的提问来源于stack exchange,提问作者lukas95
相关产品推荐
相关产品推荐

