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

使用Drag/Drop API拖拽元素后,点击删除EventListener失效如何修复?

修复拖拽后删除功能失效的问题

问题根源

拖拽交换元素时直接替换了容器的innerHTML,而通过HTML字符串插入的新.content元素丢失了原本绑定的点击删除事件——原事件是绑定在具体DOM节点上的,HTML字符串无法保留这些事件绑定关系。


方案一:事件委托(推荐)

利用事件冒泡原理,把删除事件绑定到父容器上,不管元素怎么拖拽交换,点击事件都会被父容器捕获处理。

修改后的完整代码:

<div class='my_draggable_elements'></div>
<button class='btn_add_element'>Add Element</button>

<style>
.my_draggable_elements {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.container {
  padding: 2rem;
  border: 0.1rem solid black;
  cursor: grab;
}
.container.dragged_over {
  border: 0.1rem dashed black;
}
.container > .content {
  background-color: #ddd;
  padding: 0.25rem;
  cursor: pointer;
}
.btn_add_element {
  margin-top: 2rem;
  width: 100%;
  text-align: center;
  padding: 0.5rem;
}
</style>

<script>
const btn_add_element = document.querySelector('.btn_add_element');
const my_draggable_elements = document.querySelector('.my_draggable_elements');

// 父容器绑定事件委托,统一处理删除
my_draggable_elements.addEventListener('click', (e) => {
  if (e.target.classList.contains('content')) {
    e.target.closest('.container').remove();
  }
});

function handleDragStart(e) {
  this.style.opacity = '0.4';
  dragSrcEl = this;
  e.dataTransfer.effectAllowed = 'move';
  e.dataTransfer.setData('text/html', this.innerHTML);
}
function handleDragEnd(e) {
  this.style.opacity = '1';  
  my_draggable_elements.querySelectorAll('.container').forEach(elm => {
    elm.classList.remove('dragged_over');
  });
}
function handleDragOver(e) {
  e.preventDefault();
  return false;
}
function handleDragEnter(e) {
  this.classList.add('dragged_over');
}
function handleDragLeave(e) {
  this.classList.remove('dragged_over');
}
function handleDrop(e) {
  e.stopPropagation();
  if (dragSrcEl !== this) {
    dragSrcEl.innerHTML = this.innerHTML;
    this.innerHTML = e.dataTransfer.getData('text/html');
  }
  return false;
}

function getRandomColor() { return '#'+Math.floor(Math.random()*16777215).toString(16); }

function add_element() {
  const new_container = document.createElement('div');
  new_container.classList.add('container');
  new_container.setAttribute('draggable', true);
  new_container.addEventListener('dragstart', handleDragStart);
  new_container.addEventListener('dragover', handleDragOver);
  new_container.addEventListener('dragenter', handleDragEnter);
  new_container.addEventListener('dragleave', handleDragLeave);
  new_container.addEventListener('dragend', handleDragEnd);
  new_container.addEventListener('drop', handleDrop);
  
  const new_content = document.createElement('div');
  new_content.classList.add('content');
  new_content.style.color = getRandomColor();
  new_content.innerText = 'Click to Delete';
  
  new_container.appendChild(new_content);
  my_draggable_elements.appendChild(new_container);
}

btn_add_element.addEventListener('click', () => add_element());

btn_add_element.click();
btn_add_element.click();
btn_add_element.click();
</script>

方案二:直接移动DOM元素

不通过innerHTML交换内容,而是直接移动.content节点,这样事件绑定会跟随DOM节点保留。

仅修改拖拽相关函数:

function handleDragStart(e) {
  this.style.opacity = '0.4';
  dragSrcEl = this;
  e.dataTransfer.effectAllowed = 'move';
  // 存储被拖拽的DOM节点而非HTML字符串
  e.dataTransfer.setData('text/plain', dragSrcEl.querySelector('.content'));
}

function handleDrop(e) {
  e.stopPropagation();
  if (dragSrcEl !== this) {
    // 交换两个容器内的content节点
    const srcContent = dragSrcEl.querySelector('.content');
    const targetContent = this.querySelector('.content');
    dragSrcEl.replaceChild(targetContent, srcContent);
    this.replaceChild(srcContent, targetContent);
  }
  return false;
}

方案对比

  • 事件委托:更灵活,适配所有动态元素操作场景,无需关注DOM细节。
  • 移动DOM节点:逻辑直观,保留原事件绑定,适合简单的元素交换场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 03:25:27