innerhtml 不执行 script 标签,因规范禁止其自动运行以防范 xss、避免竞态且保持语义清晰;需手动克隆并注入 script 节点才能执行。

innerHTML 不等于执行 JS,它只是解析 HTML 字符串并构建 DOM 节点,所有 <script></script> 标签(无论内联还是带 src)都会被忽略——这是浏览器的强制行为,不是 bug,也不因浏览器版本或位置( 或 )而改变。
为什么 innerHTML 插入的 <script></script> 完全不运行
W3C 规范明确禁止通过 innerHTML、insertAdjacentHTML() 等 API 插入的脚本自动执行。原因包括:
- 防止 XSS:避免恶意字符串注入后直接触发任意代码
- 避免竞态:插入时机与 DOM 加载状态脱钩,无法保证执行顺序和依赖可用性
- 统一语义:
innerHTML的职责是“渲染结构”,不是“执行逻辑”
常见错误现象:
-
container.innerHTML = '<div>ok</div> <script>console.log("run")</script>'—— 控制台无输出,也无报错 -
innerHTML = '<script src="a.js"></script>'——a.js根本不会发起网络请求(Chrome/Firefox 均如此) - 即使加了
defer或type="module",依然无效
如何安全提取并执行动态 HTML 中的脚本
核心做法是:先用 innerHTML 渲染结构,再手动遍历、克隆、注入 <script></script> 节点。关键点在于「克隆」——不能直接复用原节点,否则浏览器仍不执行。
示例函数:
function setHTMLWithScript(container, html) {
container.innerHTML = html;
const scripts = container.querySelectorAll('script');
scripts.forEach(script => {
const newScript = document.createElement('script');
// 复制属性:src、type、async、defer 等
Array.from(script.attributes).forEach(attr => {
newScript.setAttribute(attr.name, attr.value);
});
// 复制内联内容(仅当无 src)
if (!script.src) newScript.textContent = script.textContent;
// 注入到 head(或 body 底部),触发执行
document.head.appendChild(newScript);
});
}
使用注意:
- 必须在
innerHTML赋值**之后**立即执行脚本提取,否则querySelectorAll('script')找不到新节点 - 外部脚本(含
src)会正常加载并执行;内联脚本按顺序同步执行 - 重复调用时需防重入:可加
data-executed="true"标记,或清空已注入脚本
比 innerHTML 更可控的替代方案
如果业务允许,优先避开 innerHTML + script 组合,改用更清晰的职责分离方式:
- 把初始化逻辑抽成独立函数(如
initVideoPlayer()),HTML 片段只含结构,JS 在插入后手动调用 - 用
DOMParser解析 HTML 字符串,再用document.importNode()导入,但注意:它仍不执行脚本,只是提供更安全的解析入口 - 对第三方 SDK(如 Video.js、React)改用标准挂载方式:
videojs('#my-video', options),而非依赖内联脚本 - 若必须加载外部 JS,用
document.createElement('script')+src+onload显式控制时序
真正容易被忽略的是脚本执行顺序和依赖隔离:动态插入的脚本共享全局作用域,但 DOM 节点可能已被移除或重用,this 或选择器上下文极易出错。别指望“插进去就跑通”,得为每个片段设计可卸载、可重入的初始化契约。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











