使用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
相关产品推荐
相关产品推荐

