XMLHTTPRequest完成时自定义事件监听器未触发问题求助
问题:自定义事件在Hook中无法触发,绑定到window却正常工作
我尝试在XMLHTTPRequest请求完成时触发一个自定义事件,让监听器捕获该事件并启动后续操作,但事件监听器未按预期触发。将同一监听器绑定到window时可以正常工作,但在如下代码的Hook中添加时却无效。调试检查器显示事件监听器已正确添加到HTML元素中,请问我哪里出错了?
Uploaders.Cloudinary_Direct = function(entries, onViewError) { entries.forEach(entry => { let formData = new FormData() let { url, fields } = entry.meta Object.entries(fields).forEach(([key, val]) => formData.append(key, val)) formData.append("file", entry.file) formData.append("upload_preset", "avatar_upload"); let xhr = new XMLHttpRequest() onViewError(() => xhr.abort()) xhr.onload = () => { if (xhr.status === 204) { entry.progress(100) } else { entry.error() } } xhr.onerror = () => entry.error() xhr.onreadystatechange = function() { if (xhr.readyState == XMLHttpRequest.DONE) { console.log("public_id") let event = new CustomEvent("cloudinary_external_upload", { bubbles: true, cancelable: true, detail: { public_id: get_public_id(xhr) } }); this.dispatchEvent(event); // 也试过window.dispatchEvent,但没用 } } xhr.upload.addEventListener("progress", (event) => { if (event.lengthComputable) { let percent = Math.round((event.loaded / event.total) * 100) if (percent < 100) { entry.progress(percent) } else { entry.progress(100) } } }) xhr.open("POST", url, true) xhr.send(formData) }) } Hooks.ExternalUpload = { mounted() { // 确保监听器只在组件挂载后添加 this.el.addEventListener('cloudinary_external_upload', e => { console.log("cloudinary_upload") // 从未打印 this.pushEvent("external_upload", { id: this.el.id, public_id: e.originalEvent.detail.public_id }); }); } } let liveSocket = new LiveSocket("/live", Socket, { hooks: Hooks, uploaders: Uploaders })
编辑说明:调试检查器显示事件监听器已正确添加到HTML元素中。
解决方案
核心问题:事件触发的目标错误
你在xhr.onreadystatechange回调里用this.dispatchEvent(event),这里的this指向的是XMLHttpRequest实例,而非挂载监听器的DOM元素。事件被触发在xhr对象上,而Hook的监听器绑在DOM元素上,自然捕获不到。
具体修复方法
方法1:修改事件触发目标为全局对象(快速解决)
如果不需要精准触发到单个元素,可以把事件触发到document或window上,同时修改Hook的监听目标:
// 修改xhr回调里的事件触发代码 window.dispatchEvent(event); // 修改Hook的监听逻辑 Hooks.ExternalUpload = { mounted() { document.addEventListener('cloudinary_external_upload', e => { console.log("cloudinary_upload") this.pushEvent("external_upload", { id: this.el.id, public_id: e.detail.public_id // 无需originalEvent,直接取CustomEvent的detail }); }); }, destroyed() { // 组件销毁时移除监听器,避免内存泄漏 document.removeEventListener('cloudinary_external_upload', ...) } }
方法2:传递目标DOM元素(精准触发)
如果Uploader和Hook对应的DOM元素是关联的,最好在初始化Uploader时传入目标元素引用:
// 修改Uploader函数,新增targetEl参数 Uploaders.Cloudinary_Direct = function(entries, onViewError, targetEl) { entries.forEach(entry => { // ... 其他代码不变 xhr.onreadystatechange = function() { if (xhr.readyState == XMLHttpRequest.DONE) { console.log("public_id") let event = new CustomEvent("cloudinary_external_upload", { bubbles: true, cancelable: true, detail: { public_id: get_public_id(xhr) } }); targetEl.dispatchEvent(event); // 直接触发到目标DOM元素 } } // ... 其他代码不变 }) } // 在Hook调用Uploader时传入this.el // (需根据实际调用Uploader的逻辑调整参数传递)
额外注意事项
- 确认
get_public_id(xhr)能正确返回值,避免因detail数据为空导致的逻辑异常 - 检查
console.log("public_id")是否打印,排除xhr回调未执行的情况 - 监听全局对象时,一定要在组件销毁时移除监听器,防止内存泄漏
内容的提问来源于stack exchange,提问作者Jason O.
相关产品推荐
相关产品推荐

