动态加载Jira服务台Widget脚本无法生成iframe的问题排查
动态加载Jira服务台Widget失败的原因与解决方案
问题描述
尝试在用户操作后通过动态创建script标签加载Jira服务台Widget脚本,代码如下:
let script = document.createElement('script'); script.setAttribute('data-jsd-embedded', ''); script.setAttribute('data-key', 'key value'); script.setAttribute('data-base-url', 'https://jsd-widget.atlassian.com'); script.setAttribute('src', 'https://jsd-widget.atlassian.com/assets/embed.js'); document.head.appendChild(script);
Chrome的Sources面板显示脚本已加载,但Widget对应的iframe并未生成,控制台也无报错。而直接在HTML的head中静态引入script标签时,一切正常:
<head> ... <script data-jsd-embedded data-key="key value" data-base-url="https://jsd-widget.atlassian.com" src="https://jsd-widget.atlassian.com/assets/embed.js"></script> </head>
原因分析
查看embed.js的未压缩源码后,核心问题出在脚本的初始化时机和识别逻辑上:
- DOMContentLoaded事件已触发:embed.js的初始化逻辑包裹在
document.addEventListener("DOMContentLoaded", () => { ... })回调中。当你在用户操作后动态插入script时,页面的DOMContentLoaded事件早已完成,这个回调根本不会执行,自然不会初始化Widget。 - 脚本识别逻辑限制:回调中首先尝试通过
document.currentScript获取当前脚本,动态加载的脚本在执行时currentScript会返回null;随后尝试用document.querySelector("script[data-jsd-embedded]")查找脚本,但由于回调没触发,这部分逻辑也不会运行。
解决方法
方法1:手动触发初始化逻辑
直接复制embed.js中的核心初始化逻辑,在动态脚本加载完成后手动执行:
function initJSDWidget() { const jsdWidgetID = "jsd-widget"; // 检查是否已存在Widget const isJsmWidgetRendered = document.querySelector(`iframe#${jsdWidgetID}`); if (isJsmWidgetRendered) { console.error("JSM Widget: could not render more than one widget on a single page"); return; } // 获取动态插入的脚本 const script = document.querySelector("script[data-jsd-embedded][data-key='key value']"); if (!script) { console.error("JSM Widget: could not find jsd-embedded script"); return; } const dataAttributes = script.dataset; if (!dataAttributes) { console.warn("JSM Widget: legacy unsupported browser, jsd-embedded script.dataset was undefined"); return; } const scriptSrc = script.getAttribute("src"); if (!scriptSrc) return; const iframeScriptSrc = `${scriptSrc.substring(0, scriptSrc.lastIndexOf("/"))}/iframe.js`; // 复制Iframe类实现 class Iframe { el; document; constructor(iframeScriptSrc, embeddableData) { this.createAndInsertIframe(document.body); this.applyCss(); this.writeDocument(iframeScriptSrc, embeddableData); } createAndInsertIframe(parent) { const iframe = document.createElement("iframe"); iframe.scrolling = "no"; iframe.name = "JSD widget"; iframe.id = jsdWidgetID; parent.appendChild(iframe); this.el = iframe; const contentWindow = iframe.contentWindow; if (!contentWindow) { console.error("Can't setup jsd-widget iframe"); throw new Error("Can't setup jsd-widget iframe"); } this.document = contentWindow.document; } applyCss() { this.css("height", "1px"); this.css("width", "1px"); this.css("position", "fixed"); this.css("border", "0"); this.css("bottom", "0"); this.css("right", "0"); this.css("z-index", "9999999999999"); } writeDocument(iframeScriptSrc, embeddableData) { const scriptTag = `<script type="application/javascript" src="${iframeScriptSrc}"></script>`; const embeddableDataJson = JSON.stringify(embeddableData); const baseURL = JSON.parse(embeddableDataJson).baseUrl; this.document.open(); this.document.write( `<html> <head><base href='${baseURL}' /></head> <body data-embeddable=${embeddableDataJson}> <div id="react-root"></div> ${scriptTag} </body> </html>` ); this.document.close(); } css(property, value) { this.el.style[property] = value; } } // 初始化Widget new Iframe(iframeScriptSrc, dataAttributes); } // 动态加载脚本并在加载完成后触发初始化 let script = document.createElement('script'); script.setAttribute('data-jsd-embedded', ''); script.setAttribute('data-key', 'key value'); script.setAttribute('data-base-url', 'https://jsd-widget.atlassian.com'); script.setAttribute('src', 'https://jsd-widget.atlassian.com/assets/embed.js'); script.onload = initJSDWidget; // 脚本加载完成后手动执行初始化 document.head.appendChild(script);
方法2:调整脚本加载时机
如果页面DOMContentLoaded事件尚未触发时就执行动态加载,可以将脚本的async属性设为false,确保脚本同步加载并触发原有的DOMContentLoaded回调,但这种方法仅适用于用户操作发生在页面加载完成前的场景,可靠性较低。
注意事项
- 页面中只能加载一个Jira服务台Widget,源码会检查是否已存在
#jsd-widget的iframe,重复加载会报错。 - 如果需要使用ReactDevTools或MutationObserverHandler功能,需从embed.js中提取对应代码并添加到初始化函数中。
内容的提问来源于stack exchange,提问作者KekmasterACR
相关产品推荐
相关产品推荐

