嵌套事件监听器异常:拖拽DOM元素位置不更新的修复方案
拖拽功能修复方案
问题核心
你的代码存在三个关键问题导致拖拽异常:
- 绑定/解绑
mousemove事件时,错误直接调用this.changePos(e),这会立即执行一次函数而非绑定监听器,导致拖拽过程中无法持续更新位置。 changePos函数的this指向错误:事件触发时this会指向DOM元素而非类实例,无法访问this.element、this.shiftX等类属性。mouseup绑定在元素上,若鼠标移出元素后松开,无法触发事件解绑,后续操作可能出现异常。
修复后的完整代码
// 保留原有的文件类型配置,已翻译为中文 video: { multiple: true, types: [ { description: "视频", accept: { "video/*": [".mp4", ".flv", ".vob", ".avi", ".wmv", ".mpg", ".gif"], }, }, ], }, SVG: { multiple: true, types: [{ description: "SVG", accept: { "image/*": [".svg"] } }], }, }; this.style_elem = function () { this.isActive ? (this.element.style.border = "2px solid #30C5FF") : (this.element.style.border = "none"); this.isActive = !this.isActive; }; this.URL_s = async function (options) { let fileHandle = await window.showOpenFilePicker(options); const fileArray = fileHandle.map(async (file) => { return file.getFile(); }); const files = await Promise.all(fileArray); const url_string = files.map((file) => { return URL.createObjectURL(file); }); return url_string; }; // 用箭头函数固定this指向类实例 this.changePos = (e) => { this.element.style.top = e.clientY - this.shiftY + "px"; this.element.style.left = e.clientX - this.shiftX + "px"; }; this.init = async function () { const URL = await this.URL_s(this.options.image); let img_arr = URL.map((url) => { const img = document.createElement("img"); img.src = url; let container = document.createElement("div"); container.style.width = "fit-content"; container.style.position = "absolute"; container.appendChild(img); this.element = container; return container; }); img_arr.forEach((container) => { container.addEventListener("mousedown", (e) => { e.preventDefault(); this.element = e.currentTarget; const rect = this.element.getBoundingClientRect(); this.shiftX = e.clientX - rect.left; this.shiftY = e.clientY - rect.top; this.style_elem(); // 首次更新位置 this.changePos(e); // 将mousemove绑定到document,确保拖拽不中断 document.addEventListener("mousemove", this.changePos); }); // mouseup绑定到document,避免鼠标移出元素后无法解绑 document.addEventListener("mouseup", () => { this.style_elem(); document.removeEventListener("mousemove", this.changePos); }, { once: true }); // 确保该监听器仅执行一次,避免重复绑定 document .getElementById("ifr") .contentWindow.document.body.appendChild(container); }); }; this.init(); } }
关键修复细节
- 修正事件绑定逻辑:
把this.element.addEventListener("mousemove",this.changePos(e))改为document.addEventListener("mousemove", this.changePos),直接传递函数引用而非调用函数;解绑时使用同一个引用,确保能正确移除监听器。 - 固定this指向:
将changePos改为箭头函数,确保函数内部的this始终指向类实例,而非触发事件的DOM元素,保证能访问类的属性。 - 调整事件绑定对象:
将mousemove和mouseup绑定到document上,避免鼠标移出拖拽元素后功能中断;给mouseup监听器添加{ once: true }选项,防止重复绑定导致的多次触发问题。
内容的提问来源于stack exchange,提问作者Kleokamoto
相关产品推荐
相关产品推荐

