拖拽元素时实现复制功能:移至另一列表且保留原元素
拖拽复制功能实现方案
当前拖拽功能为移动元素,拖拽后原列表的元素会被移除。需求调整为:拖拽元素到目标列表时,原列表保留该元素,目标列表新增该元素(即复制而非移动)。
效果对比
拖拽前状态

当前拖拽后(不符合需求)

期望拖拽后效果

现有代码
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link rel="stylesheet" href="styles.css"> <script src="script.js" defer></script> <title>Document</title> </head> <body> <div class="container"> <p class="draggable" draggable="true">1</p> <p class="draggable" draggable="true">2</p> </div> <div class="container"> <p class="draggable" draggable="true">3</p> <p class="draggable" draggable="true">4</p> </div> </body> </html>
CSS
body { margin: 0; } .container { background-color: #333; padding: 1rem; margin-top: 1rem; } .draggable { padding: 1rem; background-color: white; border: 1px solid black; cursor: move; } .draggable.dragging { opacity: .5; }
JavaScript
const draggables = document.querySelectorAll('.draggable'); const containers = document.querySelectorAll('.container'); draggables.forEach(draggable => { draggable.addEventListener('dragstart', () => { draggable.classList.add('dragging'); }) draggable.addEventListener('dragend', () => { draggable.classList.remove('dragging'); }) }) containers.forEach(container => { container.addEventListener('dragover', e => { e.preventDefault(); const afterElement = getDragAfterElement(container, e.clientY); const draggable = document.querySelector('.dragging'); if (afterElement == null) { container.appendChild(draggable); } else { container.insertBefore(draggable, afterElement); } }) }) function getDragAfterElement(container, y) { const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')] return draggableElements.reduce((closest, child) => { const box = child.getBoundingClientRect(); const offset = y - box.top - box.height / 2; if (offset < 0 && offset > closest.offset) { return { offset: offset, element: child } } else { return closest } }, { offset: Number.NEGATIVE_INFINITY }).element }
修改方案
核心逻辑:将移动原元素改为克隆元素并添加到目标容器,原元素保留在原列表中。
修改后的JavaScript代码:
const draggables = document.querySelectorAll('.draggable'); const containers = document.querySelectorAll('.container'); // 封装拖拽事件绑定函数,避免重复代码 function bindDragEvents(element) { element.addEventListener('dragstart', () => { element.classList.add('dragging'); }); element.addEventListener('dragend', () => { element.classList.remove('dragging'); }); } // 给初始元素绑定拖拽事件 draggables.forEach(bindDragEvents); containers.forEach(container => { container.addEventListener('dragover', e => { e.preventDefault(); const afterElement = getDragAfterElement(container, e.clientY); const draggable = document.querySelector('.dragging'); // 深拷贝原拖拽元素,复制所有内容和属性 const clonedElement = draggable.cloneNode(true); // 给克隆元素绑定拖拽事件 bindDragEvents(clonedElement); // 将克隆元素插入目标容器 if (afterElement == null) { container.appendChild(clonedElement); } else { container.insertBefore(clonedElement, afterElement); } }) }) function getDragAfterElement(container, y) { const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')] return draggableElements.reduce((closest, child) => { const box = child.getBoundingClientRect(); const offset = y - box.top - box.height / 2; if (offset < 0 && offset > closest.offset) { return { offset: offset, element: child } } else { return closest } }, { offset: Number.NEGATIVE_INFINITY }).element }
修改说明
- 使用
cloneNode(true)深拷贝原元素,确保新元素与原元素完全一致 - 封装
bindDragEvents函数,统一绑定拖拽事件,避免重复代码 - 给克隆后的新元素绑定拖拽事件,保证新元素也能正常拖拽
- 目标容器插入的是克隆元素,原元素不会被移除,实现复制效果
内容的提问来源于stack exchange,提问作者Yegor Oshlakov
相关产品推荐
相关产品推荐

