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

JavaScript拖拽模板优化:阻止左侧区块移除并支持多次拖拽

解决方案

问题根源是你直接将左侧的原始DOM元素移动到了右侧容器,导致左侧元素被移除。要实现复制拖拽(而非移动),核心是在拖拽完成时创建原元素的克隆版本,再将克隆添加到右侧容器。

修改核心逻辑

1. 规范拖拽数据传递

在dragstart事件中,通过dataTransfer传递区块ID,让右侧能准确获取对应区块的数据,避免依赖DOM元素属性:

draggable.addEventListener('dragstart', (e) => {
  draggable.classList.add('dragging');
  // 传递区块ID到拖拽数据中
  e.dataTransfer.setData('text/plain', draggable.getAttribute('data-block'));
});

2. 拆分拖拽事件职责

按照拖拽API标准,dragover仅负责阻止默认行为以触发drop,实际添加元素的逻辑放到drop事件中:

// 替换原dragover事件监听
document.right.addEventListener('dragover', (e) => {
  e.preventDefault(); // 必须阻止默认行为才能触发drop
});

// 添加drop事件监听
document.right.addEventListener('drop', (e) => {
  e.preventDefault();
  const blockId = e.dataTransfer.getData('text/plain');
  const targetBlock = blocksData.find(block => block.id == blockId);
  
  if (!targetBlock) return;
  
  // 创建新的区块元素(而非移动原元素)
  const newBlock = document.createElement('div');
  newBlock.className = 'block';
  newBlock.setAttribute('data-block', targetBlock.id);
  newBlock.innerHTML = targetBlock.htmlContent;
  
  // 确定插入位置
  const afterElement = getDragAfterElement(document.right, e.clientY);
  if (afterElement) {
    document.right.insertBefore(newBlock, afterElement);
  } else {
    document.right.appendChild(newBlock);
  }
});

3. 移除错误的DOM移动逻辑

删除原dragover事件中appendChild和insertBefore操作原DOM元素的代码,不再移动左侧的原始区块。

完整修改后的JavaScript代码

const blocksData = [{
    id: 1,
    label: 'Test Content',
    htmlContent: '<h1>Test Content</h1>'
  },
  {
    id: 2,
    label: 'Test Content',
    htmlContent: '<h2>Test Content</h2>'
  },
  {
    id: 3,
    label: 'Test Content',
    htmlContent: '<h3>Test Content</h3>'
  },
  {
    id: 4,
    label: 'Test Content',
    htmlContent: '<button>Test Content</button>'
  },
];
const getDragAfterElement = (container, y) => {
  const draggableElements = [...container.querySelectorAll('.block: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
}
const initiateHandler = () => {
  const left = document.getElementsByClassName("left")[0];
  const handle = document.getElementById("handle");
  handle.addEventListener("mousedown", (e) => {
    let ismdwn = 1
    const mV = (event) => {
      if (ismdwn === 1) {
        left.style.flexBasis = event.clientX + "px"
      } else {
        end()
      }
    }
    const end = () => {
      ismdwn = 0
      document.body.removeEventListener('mousemove', mV)
      document.body.removeEventListener('mouseup', end)
    }
    document.body.addEventListener('mousemove', mV)
    document.body.addEventListener('mouseup', end)
  });
}
const onReady = () => {
  initiateHandler();
  const drggableBlocksHolder = document.getElementById("blocks");
  // 简化区块渲染逻辑
  blocksData.forEach((block) => {
    drggableBlocksHolder.innerHTML += `<div draggable="true" class="block" data-block="${block.id}">${block.htmlContent}</div>`;
  });
  const rightContainer = document.getElementsByClassName("right")[0];
  const draggables = document.querySelectorAll('#blocks .block');
  
  draggables.forEach(draggable => {
    draggable.addEventListener('dragstart', (e) => {
      draggable.classList.add('dragging');
      e.dataTransfer.setData('text/plain', draggable.getAttribute('data-block'));
    });
    draggable.addEventListener('dragend', () => {
      draggable.classList.remove('dragging');
    });
  });
  
  rightContainer.addEventListener('dragover', (e) => {
    e.preventDefault();
  });
  
  rightContainer.addEventListener('drop', (e) => {
    e.preventDefault();
    const blockId = e.dataTransfer.getData('text/plain');
    const targetBlock = blocksData.find(block => block.id == blockId);
    
    if (!targetBlock) return;
    
    const newBlock = document.createElement('div');
    newBlock.className = 'block';
    newBlock.setAttribute('data-block', targetBlock.id);
    newBlock.innerHTML = targetBlock.htmlContent;
    
    const afterElement = getDragAfterElement(rightContainer, e.clientY);
    if (afterElement) {
      rightContainer.insertBefore(newBlock, afterElement);
    } else {
      rightContainer.appendChild(newBlock);
    }
  });
};

(function() {
  onReady()
})();

额外优化

  • 将全局变量ismdwn改为局部变量,避免全局污染
  • 简化区块渲染的冗余判断逻辑
  • 修正了end函数中事件监听移除的错误

修改后,左侧区块不会被移除,每次拖拽都会在右侧生成新的副本,支持多次重复拖拽。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 10:07:05