You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

拖拽元素时实现复制功能:移至另一列表且保留原元素

拖拽复制功能实现方案

当前拖拽功能为移动元素,拖拽后原列表的元素会被移除。需求调整为:拖拽元素到目标列表时,原列表保留该元素,目标列表新增该元素(即复制而非移动)。

效果对比

拖拽前状态

拖拽前效果

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

当前拖拽后效果

期望拖拽后效果

期望拖拽后效果


现有代码

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
}

修改说明

  1. 使用cloneNode(true)深拷贝原元素,确保新元素与原元素完全一致
  2. 封装bindDragEvents函数,统一绑定拖拽事件,避免重复代码
  3. 给克隆后的新元素绑定拖拽事件,保证新元素也能正常拖拽
  4. 目标容器插入的是克隆元素,原元素不会被移除,实现复制效果

内容的提问来源于stack exchange,提问作者Yegor Oshlakov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 13:40:38