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

动态加载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的未压缩源码后,核心问题出在脚本的初始化时机和识别逻辑上:

  1. DOMContentLoaded事件已触发:embed.js的初始化逻辑包裹在document.addEventListener("DOMContentLoaded", () => { ... })回调中。当你在用户操作后动态插入script时,页面的DOMContentLoaded事件早已完成,这个回调根本不会执行,自然不会初始化Widget。
  2. 脚本识别逻辑限制:回调中首先尝试通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 00:44:54