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

HTML Draggable问题:拖拽结束后虚影返回原位置如何解决

解决拖拽重排后虚影返回原位置的问题

这个虚影是浏览器默认拖拽机制生成的元素镜像——哪怕你已经移动了原元素,它依然会绑定初始位置,在dragend时触发回位动画。以下是具体修复方案:

核心修复步骤

  • 在dragstart事件中,必须设置event.dataTransfer.setData(哪怕只是传递空内容),同时指定拖拽行为类型effectAllowed = 'move',让浏览器正确识别拖拽意图
  • 可选:用setDragImage隐藏默认拖拽镜像,彻底消除虚影干扰

修改后的关键代码片段:

song.addEventListener("dragstart", (event) => {
  draggedElement = event.target;
  draggedText = draggedElement.textContent;
  draggedElement.classList.add("dragging");
  // 必须设置dataTransfer内容,否则浏览器会保留默认镜像
  event.dataTransfer.setData('text/plain', draggedText);
  // 指定拖拽行为为移动
  event.dataTransfer.effectAllowed = 'move';
  // 隐藏默认拖拽镜像(用透明空图片实现)
  const emptyImg = new Image();
  event.dataTransfer.setDragImage(emptyImg, 0, 0);
});

另外,原代码中dragover事件的元素移动逻辑会在鼠标划过每个元素时频繁触发,建议优化为只在鼠标穿过元素中点时才调整位置,减少不必要的DOM操作:

song.addEventListener("dragover", (event) => {
  cancelDefault(event);
  if (!draggedElement || event.target === draggedElement) return;

  const targetRect = event.target.getBoundingClientRect();
  // 计算元素中点,判断插入方向
  const midPoint = targetRect.top + targetRect.height / 2;
  if (event.clientY > midPoint) {
    event.target.after(draggedElement);
  } else {
    event.target.before(draggedElement);
  }
});

完整修复代码

HTML

<div id='song-list' class='title-list'>
</div>

CSS

.draggable-song {
  height: 26px;
  padding: 4px 8px;
  border: 1px solid #eee;
  margin: 4px 0;
  cursor: grab;
}

.draggable-song:active {
  cursor: grabbing;
}

.dragging {
  opacity: 0.4;
  transform: translateX(-9999px);
}

JavaScript

let cancelDefault = (e) => {
  e.preventDefault();
  e.stopPropagation();
  return false;
};

let list = document.getElementById('song-list');
let draggedElement = null;
let draggedText = null;

let createSongElement = (songData) => {
  let song = document.createElement('div');
  song.classList.add('draggable-song');
  song.setAttribute('draggable', true);
  song.textContent = songData[1];

  song.addEventListener("dragstart", (event) => {
    draggedElement = event.target;
    draggedText = draggedElement.textContent;
    draggedElement.classList.add("dragging");
    // 修复核心:设置dataTransfer内容
    event.dataTransfer.setData('text/plain', draggedText);
    event.dataTransfer.effectAllowed = 'move';
    // 隐藏默认拖拽镜像
    const emptyImg = new Image();
    event.dataTransfer.setDragImage(emptyImg, 0, 0);
  });

  song.addEventListener("dragend", (event) => {
    draggedElement?.classList.remove("dragging");
    draggedElement = null;
    cancelDefault(event);
  });

  song.addEventListener("dragenter", cancelDefault);
  song.addEventListener("drop", cancelDefault);

  song.addEventListener("dragover", (event) => {
    cancelDefault(event);
    if (!draggedElement || event.target === draggedElement) return;

    const targetRect = event.target.getBoundingClientRect();
    const midPoint = targetRect.top + targetRect.height / 2;
    if (event.clientY > midPoint) {
      event.target.after(draggedElement);
    } else {
      event.target.before(draggedElement);
    }
  });

  return song;
};

let songs = [
  ['1234', 'Garden on the kitchen floor'],
  ['1423', 'Never say never'],
  ['3251', 'Sitting, waiting, wishing'],
  ['412', 'Murder on the dance floor'],
  ['3251421', 'To the moon and back'],
];

songs.forEach((songData) => {
  let song = createSongElement(songData);
  list.appendChild(song);
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 22:23:13