解决拖拽到列表最后一项时dragenter事件多次触发重复创建元素问题
解决拖拽排序列表最后一项上方重复创建元素的问题
我在开发JavaScript拖拽排序列表时,遇到一个问题:将元素拖拽到列表最后一项上方时,会在该元素下方多次创建虚线占位的dropzone元素,需要实现拖拽到最后一项时仅创建一个元素。
修改后的完整代码
HTML
<div id="list"> <ul> <li draggable="true" data-value="1">1</li> <li draggable="true" data-value="2">2</li> <li draggable="true" data-value="3">3</li> <li draggable="true" data-value="4">4</li> </ul> </div>
CSS
#list { width: 200px; margin: 100px; background: #ccc; padding-bottom: 50px; } #list ul { list-style: none; padding: 0; } #list ul li { display: block; padding: 10px; margin: 3px; background-color: #222; color: white; width: 50px; height: 25px; border-top: 1px solid white; transition: all 0.2s ease-out; } #list ul li:hover { background: #444; cursor: move; } #list ul li.dragover { border-top-width: 0px; transition: all 0.2s ease-out; } .dragStartClass { opacity: 35%; transition: all 0.2s ease-out; overflow: hidden; } #list ul li.dropzone { width: 50px; height: 25px; background: none; border: 3px dashed #d00000; }
JavaScript
const list = document.getElementById('list'); const ul = list.querySelector('ul'); function startDrag(event) { event.target.classList.add("dragStartClass"); event.dataTransfer.setData('dragPoint', event.target.dataset.value); event.dataTransfer.dropEffect = 'move'; } function drag(event) { event.preventDefault(); } function dragOver(event) { event.preventDefault(); } function dragEnter(event) { // 只处理实际的可拖拽li元素 const targetLi = event.target.closest('[data-value]'); if (!targetLi) return; targetLi.classList.add("dragover"); // 先移除所有已存在的dropzone,避免重复创建 const existingDropzones = document.querySelectorAll('.dropzone'); existingDropzones.forEach(zone => zone.remove()); const newNode = document.createElement('li'); newNode.className = 'dropzone'; const dropVar = parseInt(targetLi.dataset.value); const currentLiCount = ul.querySelectorAll('[data-value]').length; if (dropVar < currentLiCount) { ul.insertBefore(newNode, targetLi); } else if (dropVar === currentLiCount) { ul.appendChild(newNode); } } function dragLeave(event) { const targetLi = event.target.closest('[data-value]'); if (targetLi) { targetLi.classList.remove("dragover"); } // 离开元素时清理所有dropzone const existingDropzones = document.querySelectorAll('.dropzone'); existingDropzones.forEach(zone => zone.remove()); } function drop(event) { event.preventDefault(); // 清理dropzone并完成拖拽逻辑(可根据需求补充排序逻辑) const existingDropzones = document.querySelectorAll('.dropzone'); existingDropzones.forEach(zone => zone.remove()); } function dragEnd(event) { event.target.classList.remove("dragStartClass"); // 拖拽结束后清理所有dropzone const existingDropzones = document.querySelectorAll('.dropzone'); existingDropzones.forEach(zone => zone.remove()); } document.addEventListener('dragstart', startDrag); document.addEventListener('drag', drag); document.addEventListener('dragover', dragOver); document.addEventListener('dragenter', dragEnter); document.addEventListener('dragleave', dragLeave); document.addEventListener('dragend', dragEnd); document.addEventListener('drop', drop);
关键修改说明
- 避免重复创建dropzone:每次进入元素创建新dropzone前,先移除页面上所有已存在的dropzone,确保始终只有一个占位元素。
- 动态获取列表长度:用
ul.querySelectorAll('[data-value]').length替代静态的初始列表长度,确保最后一项的判断始终准确。 - 优化事件目标判断:用
closest('[data-value]')确保只处理实际的可拖拽li元素,避免事件冒泡导致的误触发。 - 完善清理逻辑:在dragLeave、drop、dragEnd事件中都添加dropzone清理,避免拖拽过程中残留多余元素。
内容的提问来源于stack exchange,提问作者DumbMathBoy
相关产品推荐
相关产品推荐

