二次加载弹窗下拉切换出现Cannot read property 'done' of undefined报错
问题原因
核心是事件重复绑定+全局请求状态变量污染两个问题共同导致:
- 每次加载弹窗时你都给下拉选择框重新绑定了一次
change事件,但关闭弹窗移除DOM时没有解绑之前绑定的事件,第二次打开弹窗后切换下拉会同时触发两次绑定的回调函数,先后两次调用makeRequest - 你封装的
makeRequest里用了全局/外层作用域的inProgressRequest变量做请求防重:第一次回调调用makeRequest时会把inProgressRequest设为true,紧接着触发的第二次回调调用makeRequest时,检测到inProgressRequest为true就会走到else分支,没有返回Promise对象,你在调用makeRequest后链式调用的.done()就会抛出“Cannot read property 'done' of undefined”错误。
解决方案
你可以根据自己的业务场景选任意一种方案,也可以组合使用提升兼容性:
- 方案1:绑定事件前先解绑原有事件
每次给下拉框绑定change事件前,先移除之前绑定的事件,避免重复触发:// 弹窗加载完成后绑定事件的代码修改为 $('#你的下拉框ID').off('change').on('change', function() { makeRequest(/* 你的请求参数 */).done(function(res) { // 原有业务逻辑 }) }) - 方案2:使用事件委托仅绑定一次
不需要每次加载弹窗都绑定事件,直接在页面初始化时给页面固定容器做事件委托,一次绑定就可以适配后续动态加载的下拉元素:// 页面初始化时仅执行一次即可 $('body').on('change', '#弹窗内下拉框的ID', function() { makeRequest(/* 你的请求参数 */).done(function(res) { // 原有业务逻辑 }) }) - 方案3:修复
makeRequest的返回值兼容
即使命中防重逻辑,也返回一个已完成的Promise对象,避免调用.done时报错:function makeRequest(/* 你的入参 */) { if (inProgressRequest) { // 返回已经resolve的Deferred对象,不会触发链式调用报错 return $.Deferred().resolve().promise() } inProgressRequest = true return $.ajax(/* 你的请求配置 */).always(function() { inProgressRequest = false }) } - 方案4:替换全局防重逻辑
如果makeRequest是多场景共用的工具函数,不要用全局的inProgressRequest,改用请求URL+参数的哈希值做Key,用Map存储不同请求的防重状态,避免不同业务的请求互相干扰。
内容的提问来源于stack exchange,提问作者Tóth András
相关产品推荐
相关产品推荐

