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

如何使用原生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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:52:21