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

点击按钮动态添加脚本并调用方法遇两类问题求助

动态加载聊天脚本并调用方法的问题修复

问题场景

我希望点击按钮时动态添加聊天窗口的脚本,并调用该脚本中的方法,目前实现的代码如下:

原JavaScript代码

const callExternalScript = url => {
  let script
  script = document.querySelector(`script[src=\"${url}\"]`)

  if (!script) {
    script = document.createElement('script')
    script.type = 'application/javascript'
    script.src = url
    script.async = true
    document.body.appendChild(script)
  }


  return new Promise((res, rej) => {
    script.addEventListener('load', function() {
      res()
    })
    script.addEventListener('error', function() {
      rej('error')
    })
  })
}

const handleClick = () => {
  callExternalScript('http://abcd.js')
    .then(() => {
      abc() // method from abcd js;
    })
    .catch(err => {
      console.log(err)
    })
}

原HTML代码

<button onClick="handleClick()">Click Me</button>

存在的问题

  • 脚本首次加载时可正常运行,但脚本已加载后再次点击按钮无法正常工作
  • 无法调用abc()方法:因为abcd.js内部会加载另一个包含该方法的脚本,当前代码未等待这个嵌套脚本加载完成就执行调用

解决方案

1. 修复重复加载后的执行问题

通过缓存已加载完成的脚本URL,避免重复绑定load事件(已加载的脚本不会再次触发load事件),直接返回已完成状态。

2. 等待嵌套脚本加载完成

新增轮询函数,检查全局目标方法是否可用,确保嵌套脚本加载完成后再执行调用,同时添加超时机制防止无限等待。

修改后的完整代码

JavaScript部分

// 缓存已加载完成的脚本URL
const loadedScripts = new Set();

const callExternalScript = url => {
  return new Promise((res, rej) => {
    // 脚本已加载完成,直接resolve
    if (loadedScripts.has(url)) {
      res();
      return;
    }

    let script = document.querySelector(`script[src="${url}"]`);

    if (!script) {
      script = document.createElement('script');
      script.type = 'application/javascript';
      script.src = url;
      script.async = true;
      document.body.appendChild(script);
    }

    // 检查脚本是否已加载完成(之前添加但未绑定事件的情况)
    if (script.readyState === 'complete' || script.readyState === 'loaded') {
      loadedScripts.add(url);
      res();
      return;
    }

    script.addEventListener('load', function() {
      loadedScripts.add(url);
      res();
    });
    script.addEventListener('error', function() {
      rej(`加载脚本 ${url} 失败`);
    });
  });
};

// 等待指定全局方法可用,默认超时10秒
const waitForMethod = (methodName, timeout = 10000) => {
  return new Promise((res, rej) => {
    const checkInterval = setInterval(() => {
      if (typeof window[methodName] === 'function') {
        clearInterval(checkInterval);
        res();
      }
    }, 100);

    // 超时处理
    setTimeout(() => {
      clearInterval(checkInterval);
      rej(`等待方法 ${methodName} 超时`);
    }, timeout);
  });
};

const handleClick = () => {
  callExternalScript('http://abcd.js')
    .then(() => waitForMethod('abc'))
    .then(() => {
      abc(); // 安全调用目标方法
    })
    .catch(err => {
      console.error(err);
    });
};

HTML部分

<button onClick="handleClick()">Click Me</button>

内容的提问来源于stack exchange,提问作者Master.Deep

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 09:03:25