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

鼠标进入拖放区域时DragEnter事件触发不稳定的解决咨询

可靠的文件拖放区域脉动提示解决方案

原代码的问题出在:拖放区域container内部包含子元素(div1和<p>),当拖动文件进入子元素时,会触发container的dragleave事件,同时触发子元素的dragenter,导致脉动效果频繁开启/关闭,看起来像是dragenter触发不可靠。

最稳妥的解决方式是用计数器跟踪进入次数,只有当真正进入整个拖放区域(计数器从0→1)时激活脉动,真正离开(计数器从1→0)时关闭脉动。以下是修改后的完整代码:

HTML(无需修改)

<h1>File Drop Upload Example</h1>

<p>Drag your file(s) into the Drop Zone</p>

<div id="container">
  <div id="div1">
    <p>File Drop Zone</p>
  </div>
  <div id="div2"></div>
</div>

CSS(无需修改)

#container {
  position: relative;
  display: flex;
  width: 120px;
  height: 120px;
  padding: 10px;
  border: 1px solid black;
}

#div1 {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  background-color: lightblue;
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 300 300'><path fill='%23FFF' stroke='%23E50024' stroke-width='50' d='M150 25 a125 125 0 1 0 2 0z m 2 100 a25 25 0 1 1-2 0z'/></svg>"), auto;
}

#div2 {
  --fade-time: 0;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0%;
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 300 300'><path fill='%23FFF' stroke='%23E50024' stroke-width='50' d='M150 25 a125 125 0 1 0 2 0z m 2 100 a25 25 0 1 1-2 0z'/></svg>");
  animation: pulseImage var(--fade-time) infinite;
}

body>p {
  color: red;
}

div>p {
  font-weight: bold;
}

@keyframes pulseImage {
  from {
    opacity: 0;
  }

  to {
    opacity: 0.7;
  }
}

修改后的JavaScript

document.addEventListener("DOMContentLoaded", (e) => {
  // 阻止全局拖放默认行为,避免浏览器打开文件
  document.addEventListener('drop', function(e) {
    e.preventDefault()
  })
  document.addEventListener('dragenter', function(e) {
    e.preventDefault();
    e.dataTransfer.dropEffect = "none"
  })
  document.addEventListener('dragover', function(e) {
    e.preventDefault();
    e.dataTransfer.dropEffect = "none"
  })

  const dropZone = document.getElementById("container")
  const pulseElement = document.getElementById("div2")
  let enterCount = 0; // 跟踪进入拖放区域的次数

  dropZone.addEventListener("dragenter", (e) => {
    e.dataTransfer.dropEffect = "copy"
    e.preventDefault()
    e.stopPropagation()
    
    enterCount++;
    // 只有第一次进入时激活脉动
    if (enterCount === 1) {
      pulseElement.style.setProperty("--fade-time", "2.0s")
    }
  })

  dropZone.addEventListener("dragover", (e) => {
    e.dataTransfer.dropEffect = "copy"
    e.preventDefault()
    e.stopPropagation()
  })

  dropZone.addEventListener("dragleave", (e) => {
    e.preventDefault();
    e.stopPropagation()
    
    enterCount--;
    // 只有完全离开时关闭脉动
    if (enterCount === 0) {
      pulseElement.style.removeProperty("--fade-time")
    }
  })

  dropZone.addEventListener("drop", catchFiles)
})

function catchFiles(e) {
  e.preventDefault();
  document.getElementById("div2").style.removeProperty("--fade-time")
  console.log("File(s) dropped");

  let fileHolder = new FormData()
  let fileCount = 0

  if (e.dataTransfer.items) {
    [...e.dataTransfer.items].forEach((item, i) => {
      if (item.kind === "file") {
        const file = item.getAsFile()
        console.log(`… file[${i}].name = ${file.name}`)
        fileHolder.append("file" + i, file, file.name)
        fileCount++
      }
    });
  } else {
    [...e.dataTransfer.files].forEach((file, i) => {
      console.log(`… file[${i}].name = ${file.name}`);
      fileHolder.append("file" + i, file, file.name)
      fileCount++
    });
  }
  if (fileCount == 0) {
    alert("Zero files received")
    return
  }
  alert("got " + fileCount + " files")
  
  // 以下上传代码可根据需要启用
  /*
  const abortControl = new AbortController();
  const signal = abortControl.signal;
  const fetchOpts = {
    signal: signal,
    method: 'POST',
    body: fileHolder,
    cache: "no-cache"
  };
  fetch("https://localhost:7196/upload/PostFormData", fetchOpts)
    .catch(err => {
      console.log("Upload failed: " + err.message);
    });
  */
}

关键修改说明

  1. 添加计数器enterCount:解决子元素干扰问题——进入子元素时dragenter会触发多次,计数器递增;离开子元素时dragleave触发,计数器递减,只有计数器回到0时才认为真正离开了整个拖放区域。
  2. 优化状态切换逻辑:只在enterCount从0变为1时激活脉动,从1变为0时关闭脉动,避免频繁切换。
  3. 缓存DOM元素:提前获取div2元素,避免重复调用getElementById提升性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 14:52:04