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

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.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 03:12:24