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}}
方案二:推荐的全局注册写法(性能更优,无重复执行问题)
- 进入XWiki后台,创建一个类型为
JavaScript Extension的页面,把上面{{javascript}}宏内的逻辑全部粘贴进去,设置该JSX对所有页面生效 - 宏内只需要输出对应class的img标签即可,不需要再嵌入脚本,这种方式不会被任何编辑器过滤,全局仅加载一次脚本
额外注意事项
- 不要用
className == 'drawio'判断,改用classList.contains('drawio'),避免元素有其他class的时候判断失效 - 记得给drawio的服务地址配置CORS规则,否则postMessage通信会被浏览器拦截
- 后续保存修改后的图片内容到XWiki侧,需要调用XWiki的REST API更新当前页面的宏内容参数
内容的提问来源于stack exchange,提问作者WestFarmer
相关产品推荐
相关产品推荐

