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

Vanilla JavaScript拖拽功能二次拖拽失效问题求助

拖拽功能首次正常后续失效问题解决

问题描述

使用Vanilla JavaScript实现Web应用拖拽功能,需求是将元素拖放后移动到div内的新位置,但遇到以下问题:

  • 首次拖放操作正常,后续无法再次进行拖放
  • 调试发现:首次拖拽元素时无内联样式(见附录A);第二次拖拽时元素已带有内联样式(见附录B),且无法修改这些样式值
  • 手动为可拖拽元素添加内联样式后,首次拖放也会失效

代码(省略非必要部分)

JavaScript

const list = document.querySelector("#list");
const rect = list.getBoundingClientRect();
let oldLeft, oldTop, mouseXStart, mouseYStart;

function dragStart(event) {
  event.dataTransfer.setData("plain/text", event.target.id);
  const item = document.querySelector("#" + event.target.id);

  mouseXStart = event.clientX - rect.left;
  mouseYStart = event.clientY - rect.top;
  oldLeft = item.style.left;
  oldTop = item.style.top;

  console.log(item);
}

function dragOver(event) {
  event.preventDefault();
  event.dataTransfer.dropEffect = "move";
}

function dropItem(event) {
  event.preventDefault();
  const mouseXEnd = event.clientX - rect.left;
  const mouseYEnd = event.clientY - rect.top;
  //Calculate by how much mouse has been moved
  const newLeft = mouseXEnd - mouseXStart;
  const newTop = mouseYEnd - mouseYStart;
  const item = document.querySelector('#' + event.dataTransfer.getData("plain/text"));

  item.style.left = oldLeft + newLeft + "px";
  item.style.top = oldTop + newTop + "px";
}

CSS

#list {
  position: relative;
  top: 60px;
  height: 600px;
  width: 100%;
  border: 2px solid rgb(107, 14, 14);
  display: block;
}

#list>div {
  position: absolute;
  height: 200px;
  width: 200px;
  background-color: blue;
  margin: 10px;
  overflow: hidden;
  text-align: left;
}

HTML

<div id="list" ondrop="dropItem(event)" ondragover="dragOver(event)">
  <div id="test" draggable="true" ondragstart="dragStart(event)">
    <button type="button"></button>
    <div>
      <p>Test</p>
    </div>
  </div>
</div>

附录

附录A:首次调用dragStart()时console.log()输出

<div id="test" draggable="true" ondragstart="dragStart(event)">

附录B:第二次调用dragStart()时console.log()输出

<div id="test" draggable="true" ondragstart="dragStart(event)" style="left: 853px; top: 147px;">

问题原因与解决方法

核心问题

  1. 样式值拼接错误:首次拖拽时item.style.left为空字符串,拼接后生成有效CSS值;第二次拖拽时item.style.left是带px的字符串,与数字拼接会生成类似"853px100"的无效值,浏览器无法识别,导致元素位置无法更新。
  2. 容器位置未实时获取:页面加载时只计算一次list的位置,若页面滚动或容器移动,会导致拖拽计算偏差。

修复步骤

  1. 将样式值转为数字计算,空值默认取0;
  2. 在拖拽开始和结束时实时获取容器位置;
  3. 用元素dataset存储旧位置,避免全局变量污染。

修复后的代码

const list = document.querySelector("#list");
let mouseXStart, mouseYStart;

function dragStart(event) {
  event.dataTransfer.setData("plain/text", event.target.id);
  const item = document.querySelector("#" + event.target.id);
  // 实时获取容器位置
  const rect = list.getBoundingClientRect();
  
  mouseXStart = event.clientX - rect.left;
  mouseYStart = event.clientY - rect.top;
  
  // 将样式值转为数字,空值默认0,存储到元素dataset中
  item.dataset.oldLeft = parseInt(item.style.left) || 0;
  item.dataset.oldTop = parseInt(item.style.top) || 0;

  console.log(item);
}

function dragOver(event) {
  event.preventDefault();
  event.dataTransfer.dropEffect = "move";
}

function dropItem(event) {
  event.preventDefault();
  // 实时获取容器位置
  const rect = list.getBoundingClientRect();
  const mouseXEnd = event.clientX - rect.left;
  const mouseYEnd = event.clientY - rect.top;
  
  const newLeft = mouseXEnd - mouseXStart;
  const newTop = mouseYEnd - mouseYStart;
  
  const item = document.querySelector('#' + event.dataTransfer.getData("plain/text"));
  // 取出存储的旧位置进行数字计算
  const oldLeft = parseInt(item.dataset.oldLeft);
  const oldTop = parseInt(item.dataset.oldTop);
  
  item.style.left = (oldLeft + newLeft) + "px";
  item.style.top = (oldTop + newTop) + "px";
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 15:10:42