
浏览器按文档顺序解析 html,但 javascript(尤其是 `alert()`)会阻塞渲染,导致 dom 元素(如 ``)虽先出现却后显示——根本原因在于 js 执行与 ui 渲染由不同引擎处理,且 `alert()` 是同步阻塞式原生 api。
在标准 HTML 解析流程中,浏览器确实自上而下、顺序解析:遇到 <span> 就将其加入 DOM 树,遇到 <script> 就立即执行其中代码。然而,“解析完成”不等于“渲染完成”。HTML 解析器(HTML parser)负责构建 DOM,CSS 引擎负责样式计算,而渲染引擎(如 Blink 或 WebKit)负责将最终样式化的 DOM 绘制到屏幕上——这一过程存在异步延迟和批量更新机制。
关键点在于:alert() 并非普通 JavaScript 语句,而是 window 对象提供的同步阻塞式原生接口,由浏览器内核(C++ 实现)直接调用。一旦触发,它会:
- 立即中断当前 JavaScript 执行栈;
- 强制暂停所有 UI 更新(包括尚未 flush 的 DOM 渲染帧);
- 弹出模态对话框,独占用户交互焦点。
因此,尽管 <span>1111</span> 在 <script> 标签之前,浏览器可能已完成 DOM 插入,但尚未将该节点绘制到页面(即未进入渲染帧)。此时 alert("1") 被调用,UI 线程被冻结,后续所有渲染任务(包括前面 <span> 的视觉呈现)都被挂起,直到用户关闭弹窗。
✅ 验证示例(可直接运行):
立即学习“前端免费学习笔记(深入)”;
<!DOCTYPE html>
<html>
<head><title>Render vs Alert Demo</title></head>
<body>
<span style="color:red">? This should appear BEFORE alert — but won't.</span>
<script>alert("BLOCKING ALERT #1");</script>
<span style="color:green">? This appears AFTER alert — because rendering was delayed.</span>
<script>
// ✅ 解决方案:使用 setTimeout 延迟 alert,让渲染引擎有机会提交帧
setTimeout(() => alert("NON-BLOCKING ALERT #2"), 0);
</script>
</body>
</html>⚠️ 注意事项:
- setTimeout(fn, 0) 并非“立刻执行”,而是将回调推入任务队列(Task Queue),等待当前宏任务(如 HTML 解析+脚本执行)结束后,在下一个事件循环周期执行——此时浏览器通常已完成了 DOM 构建与首次样式/布局计算,具备渲染条件;
- 即使设为 10ms,实际延迟受事件循环调度影响,但至少保证了渲染机会;
- alert() 已被现代开发普遍弃用(破坏用户体验、无法自动化测试、阻塞主线程),生产环境推荐使用 console.log、自定义 modal 或 Promise-based 提示组件替代。
? 总结:HTML 标签的“位置优先级”体现在解析顺序,而非视觉呈现时序;真正决定用户何时看到内容的是渲染流水线(Rendering Pipeline)与 JavaScript 主线程的协作机制。理解这一分层模型(Parser → DOM → Style → Layout → Paint → Composite),是掌握前端性能与交互行为的基础。



















