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

解决拖拽到列表最后一项时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);

关键修改说明

  1. 避免重复创建dropzone:每次进入元素创建新dropzone前,先移除页面上所有已存在的dropzone,确保始终只有一个占位元素。
  2. 动态获取列表长度:用ul.querySelectorAll('[data-value]').length替代静态的初始列表长度,确保最后一项的判断始终准确。
  3. 优化事件目标判断:用closest('[data-value]')确保只处理实际的可拖拽li元素,避免事件冒泡导致的误触发。
  4. 完善清理逻辑:在dragLeave、drop、dragEnd事件中都添加dropzone清理,避免拖拽过程中残留多余元素。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 02:36:18