鼠标进入拖放区域时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); }); */ }
关键修改说明
- 添加计数器
enterCount:解决子元素干扰问题——进入子元素时dragenter会触发多次,计数器递增;离开子元素时dragleave触发,计数器递减,只有计数器回到0时才认为真正离开了整个拖放区域。 - 优化状态切换逻辑:只在
enterCount从0变为1时激活脉动,从1变为0时关闭脉动,避免频繁切换。 - 缓存DOM元素:提前获取
div2元素,避免重复调用getElementById提升性能。
内容的提问来源于stack exchange,提问作者McMurphy
相关产品推荐
相关产品推荐

