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

XWiki Velocity宏嵌入draw.io时内部JavaScript不生效如何解决

核心问题原因

你遇到的CKEditor模式下脚本不生效的核心原因有两个:

  • XWiki的CKEditor默认将编辑内容放在独立的<iframe>沙箱中,你直接绑定到顶层document的双击事件无法捕获iframe内部的元素点击
  • 直接在{{html}}宏内插入<script>标签会被编辑器的内容安全过滤规则剥离,就算没被剥离,脚本的执行上下文也和编辑内容的iframe上下文不一致

正确的注入方式

方案一:宏内兼容写法(无需额外创建全局资源)

把你的脚本从{{html}}里移出来,改用XWiki官方的{{javascript}}宏包装,同时兼容CKEditor的iframe上下文,修改后的宏代码如下:

{{velocity}}
#if($wikimacro.content == '')
#set ($wikimacro.content = 'iVBORw0KGgoAAAANSUhEUgAAAIMAAABHCAYAAAAk5PTEAAAC4HRFWHRteGZpbGUAJTNDbXhmaWxlJTIwaG9zdCUzRCUyMjE3Mi4yNS4xNjEuMjExJTIyJTIwbW9kaWZpZWQlM0QlMjIyMDIxLTExLTA4VDA3JTNBNDglM0EzMC4wMzdaJTIyJTIwYWdlbnQlM0QlMjI1LjAlMjAoWDExKSUyMiUyMGV0YWclM0QlMjJ4MTM1OUpQR29sZ21udEFCTFIxSCUyMiUyMHZlcnNpb24lM0QlMjIxNS42LjIlMjIlMjB0eXBlJTNEJTIyZGV2aWNlJTIyJTNFJTNDZGlhZ3JhbSUyMGlkJTNEJTIyNEd0aVp4cWRuemJnanpmMC0zXzElMjIlMjBuYW1lJTNEJTIyUGFnZS0xJTIyJTNFalpKTlQ4UWdFSVolMkZUWThtTFdqdFhsMjdidHg0cXNaNEpHVXNKTFEwTEYxYWY3MVVwbCUyRlpiT0tKNFprUFp0NGhvdnU2ZnpHc0ZXJTJCYWc0cEl6UHVJUGtlRWtEZ2wlMkZoakpFRWlTa0N5UXlraU9iQUdGJTJGQUdFTWRKT2NqaHZBcTNXeXNwMkMwdmRORkRhRFdQR2FMY04lMkI5WnElMkIyckxLcmdDUmNuVU5mMlUzSXBBTSUyRks0OENQSVNrd3ZKJTJCa3VlR28yQmVNa1o4RzRkaXRFODRqdWpkWTJXSFclMkZCeldxTiUyQmtTOGc0M3ZITmpCaHI3bjRRRFl6S1Z1OVBISlQ5JTJCdVpOOUw5enJIVmE1TU5YaHdOaXNIU1lGak80YURtT1JPS0pQVGtnTFJjdkswZXY4MGowVHRsYiUyQmxuZ1R5NEd4ME4lMkZzTTVtbjklMkY4R2RBM1dERDRFRSUyQmpEZlVqQkwwTXpGTkF0JTJCaWVUcUdLbGZZcU00Y3FydWZTaWlqZFFtT202TE9EUHQlMkZySE5QOEYlM0MlMkZkaWFncmFtJTNFJTNDJTJGbXhmaWxlJTNFUXJoXAAAAKNJREFUeJzt0rENACEQwDD2X5rvUoOe6mRL2SBrAQDAA1vjO7aZywzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzEDMQMxAzkegaNDwAA/vkAmk+zV9RvOdkAAAAASUVORK5CYII=')
#end
#if($xcontext.action == 'edit')
{{html}}
<img
  class="drawio"
  style="cursor: pointer"
  src="data:image/png;base64,$wikimacro.content"
/>
{{/html}}
{{javascript}}
// 统一的drawio双击事件绑定逻辑
function bindDrawioEvent(doc) {
    doc.addEventListener('dblclick', function(evt) {
        var url = 'http://172.25.161.211/?embed=1&ui=atlas&spin=1&modified=unsavedChanges&proto=json';
        var source = evt.srcElement || evt.target;
        if (source.nodeName == 'IMG' && source.classList.contains('drawio')) {
            if (source.drawIoWindow == null || source.drawIoWindow.closed) {
                var receive = function(evt) {
                    if (evt.data.length > 0 && evt.source == source.drawIoWindow) {
                        var msg = JSON.parse(evt.data);
                        if (msg.event == 'init') {
                            source.drawIoWindow.postMessage(JSON.stringify({action: 'load', xmlpng: source.getAttribute('src')}), '*');
                        } else if (msg.event == 'save') {
                            source.drawIoWindow.postMessage(JSON.stringify({action: 'export', format: 'xmlpng', spinKey: 'saving'}), '*');
                        } else if (msg.event == 'export') {
                            source.setAttribute('src', msg.data);
                            // 此处补充保存宏内容到XWiki服务端的逻辑
                        }
                        if (msg.event == 'exit' || msg.event == 'export') {
                            window.removeEventListener('message', receive);
                            source.drawIoWindow.close();
                            source.drawIoWindow = null;
                        }
                    }
                };
                window.addEventListener('message', receive);
                source.drawIoWindow = window.open(url);
            } else {
                source.drawIoWindow.focus();
            }
        }
    });
}
// 绑定顶层文档,兼容内联编辑器
bindDrawioEvent(document);
// 兼容CKEditor的iframe上下文,轮询找到编辑器iframe后绑定事件
var ckInterval = setInterval(function() {
    var ckIframe = document.querySelector('.cke_wysiwyg_frame');
    if (ckIframe && ckIframe.contentDocument) {
        bindDrawioEvent(ckIframe.contentDocument);
        clearInterval(ckInterval);
    }
}, 300);
{{/javascript}}
#else
{{html}}
<img src="data:image/png;base64,$wikimacro.content" />
{{/html}}
#end
{{/velocity}}

方案二:推荐的全局注册写法(性能更优,无重复执行问题)

  1. 进入XWiki后台,创建一个类型为JavaScript Extension的页面,把上面{{javascript}}宏内的逻辑全部粘贴进去,设置该JSX对所有页面生效
  2. 宏内只需要输出对应class的img标签即可,不需要再嵌入脚本,这种方式不会被任何编辑器过滤,全局仅加载一次脚本

额外注意事项

  • 不要用className == 'drawio'判断,改用classList.contains('drawio'),避免元素有其他class的时候判断失效
  • 记得给drawio的服务地址配置CORS规则,否则postMessage通信会被浏览器拦截
  • 后续保存修改后的图片内容到XWiki侧,需要调用XWiki的REST API更新当前页面的宏内容参数

内容的提问来源于stack exchange,提问作者WestFarmer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 09:24:03