如何使用原生JavaScript移除window上指定的message事件监听器
解决window message事件监听器重复绑定且无法移除的问题
我完全懂你的困扰——每次触发CKEditor的insertAEMAssets命令时,都会新增一个message监听器,可因为回调函数是在命令内部定义的,每次都是全新的函数引用,导致removeEventListener根本找不到要移除的目标。下面给你几个实用的解决思路:
思路1:固定回调函数引用(兼容所有浏览器的稳妥方案)
把receiveMessage回调移到插件init方法的作用域内,让它成为一个固定引用的函数,这样每次绑定/解绑的都是同一个函数。同时借助闭包让它能访问到editor、popup这些关键变量:
CKEDITOR.plugins.add( 'aemassetpicker', { icons: 'aemassetpicker', init: function( editor ) { CKEDITOR.scriptLoader.load( 'https://code.jquery.com/jquery-1.11.1.min.js'); var assetPickerURL = "https://localhost/aem/assetpicker"; var style = "popup"; var popup; // 将receiveMessage提升到init作用域,固定函数引用 function receiveMessage(event) { // 校验消息来源,防止恶意消息 if (assetPickerURL.indexOf(event.origin) !== 0) { return; } var fromDam = JSON.parse(event.data); console.log("fromDam", fromDam); if (fromDam.config) { var configFromDam = fromDam.config; if (configFromDam.action === 'close' || configFromDam.action === 'done') { popup?.close(); } } if (fromDam.data) { var dam_detail = fromDam.data; for (var i in dam_detail) { var title_asset = dam_detail[i].title; var url_asset = dam_detail[i].url; editor.insertHtml( '<img src="' + url_asset + '/_jcr_content/renditions/cq5dam.thumbnail.319.319.png" alt="' + title_asset + '">' ); } } // 处理完成后主动移除监听器 window.removeEventListener("message", receiveMessage, false); } editor.addCommand( 'insertAEMAssets', { exec: function() { // 先移除旧的监听器,避免重复绑定 window.removeEventListener("message", receiveMessage, false); // 绑定新的监听器 window.addEventListener("message", receiveMessage, false); // 打开弹窗 popup = window.open(assetPickerURL, "dam", "left=25%,top=25%,height=800,width=1600,status=yes,toolbar=no,menubar=no,location=yes"); } }); editor.ui.addButton( 'AEMAssetPicker', { label: 'Insert AEM Assets', command: 'insertAEMAssets', toolbar: 'basicstyles,0', allowedContent: true }); } });
这个方案的核心逻辑:
- 让
receiveMessage成为init作用域下的固定函数,每次绑定的都是同一个引用 - 执行命令前先移除旧监听器,彻底避免重复绑定问题
- 消息处理完成后再次主动移除,确保不会残留无用监听器
思路2:使用一次性监听器(现代浏览器极简方案)
如果你的项目不需要兼容IE这类旧浏览器,可以直接利用addEventListener的once选项,它会让监听器在触发一次后自动移除,省去手动解绑的麻烦:
在命令的exec方法里这样写:
window.addEventListener("message", function receiveMessage(event) { // 你的消息处理逻辑和之前完全一致... // 这里不需要手动调用removeEventListener,once选项会自动处理 }, { once: true, capture: false });
这个方案更简洁,但要注意浏览器兼容性——once是ES2019的特性,Chrome 63+、Firefox 55+、Edge 79+等现代浏览器都支持。
为什么之前的尝试无效?
你之前把回调移到顶部但失败,其实是没利用好闭包——把回调放在init作用域里,就能通过闭包访问到editor对象,完全不需要把它放在命令内部。而每次在命令内部定义回调,都会生成一个新的函数实例,导致removeEventListener找不到匹配的引用,自然无法移除。
内容的提问来源于stack exchange,提问作者Abhishek Arora
相关产品推荐
相关产品推荐

