sandbox="allow-scripts"仅允许外链脚本执行,内联、onclick、javascript:void(0)等均被静默拦截;安全预览需用Blob URL加载HTML并配allow-scripts。

直接用 sandbox="allow-scripts" 无法让内联脚本或 onclick 执行——这是设计使然,不是配置错误。真正能安全运行第三方 HTML 片段的预览环境,必须绕开内联执行、禁用同源假象、并用 Blob URL 切断 origin 关联。
为什么 sandbox="allow-scripts" 在预览里仍不执行 onclick 或 script 标签?
因为 sandbox="allow-scripts" 只允许通过 <script src="..."></script> 加载的**外链脚本**执行,所有内联行为都被硬性屏蔽:
-
<script>alert(1)</script>→ 静默跳过,控制台无报错 -
<button onclick="alert(1)">click</button>→ 点击无响应 -
<a href="javascript:alert(1)">link</a>→ 被拦截,href 不触发 -
eval()、setTimeout("alert(1)")、Function()构造器全部禁用
这不是 bug,是沙箱强制执行的“脚本来源白名单”策略:只认 src,不认内联。
如何让用户写的 HTML 片段在预览 iframe 中真正跑起来?
不能依赖内联脚本,必须把用户输入的完整 HTML 文档转成 Blob URL,再加载进带权限的 sandbox iframe:
立即学习“前端免费学习笔记(深入)”;
- 用
new Blob([htmlString], {type: 'text/html'})创建 blob 对象 - 用
URL.createObjectURL(blob)生成唯一blob:协议地址 - 设置
iframe.src为该地址,并显式声明sandbox="allow-scripts" - 避免写
allow-same-origin:blob URL 天然是独立 origin,加了反而可能被浏览器忽略或报 warning
示例关键代码:
const html = editor.value;
const blob = new Blob([html], { type: 'text/html' });
const url = URL.createObjectURL(blob);
previewIframe.src = url;
previewIframe.sandbox = 'allow-scripts'; // 不加其他 token
常见踩坑点:localStorage 看似可用,实则无效
即使开了 allow-scripts,你在预览 iframe 里调用 localStorage.setItem('x', '1') 也不会持久化:
- 沙箱 iframe 的 storage 绑定到一个临时、匿名、无持久化的 origin(哪怕用的是 blob URL)
- 刷新 iframe 后
localStorage.length就是 0,DevTools 的 Application 面板里对应条目常标为 “(inactive)” - 父页完全无法访问该 storage,也不能靠它传状态
- 若需保存用户操作,必须由父页统一管理,iframe 仅通过
window.parent.postMessage()上报
为什么不用 src="data:text/html,..."+sandbox?
虽然 data: URL 看似简单,但它在多数浏览器中会触发沙箱的最严模式:
- Chrome/Firefox:即使写了
sandbox="allow-scripts",data:URL 仍被当作 null origin,且部分 API(如fetch)静默失败 - Safari:直接拒绝加载,Network 显示
net::ERR_BLOCKED_BY_RESPONSE -
Blob URL是目前唯一被所有现代浏览器一致支持的、可稳定配合allow-scripts的方案
真正的隔离不在“加了 sandbox”,而在“切断 origin 关联 + 限定脚本入口 + 拒绝内联通道”。预览引擎里最易被忽略的,是开发者总想保留 onclick 和 <script></script> 标签的写法习惯——那恰恰是沙箱第一道防线要拦住的东西。



















