前端杂记202607
前端杂记202607
setTimeout 与 requestAnimationFrame 的区别
| 特性 | setTimeout | requestAnimationFrame |
|---|---|---|
| 调度机制 | 基于时间,指定多少毫秒后执行 | 基于屏幕刷新率,在下次重绘前执行 |
| 最小延迟 | 嵌套超过一定层级后至少 4ms,后台标签页可能被节流到 1s | 通常 16.7ms(60Hz),自动与刷新率对齐 |
| 是否与渲染同步 | 不同步,可能在两次渲染之间执行,导致丢帧或多余计算 | 完全同步,保证回调在渲染帧开始时运行,适合视觉更新 |
| 后台标签页行为 | 延迟会被浏览器大幅延长,降低功耗但可能造成卡顿感 | 自动暂停,标签页不可见时不执行,恢复后继续 |
| 适用场景 | 延迟执行、非视觉逻辑、精确时间控制 | 动画、滚动/选区变化的实时 UI 更新、节流高频事件 |
简单总结:
- 如果你想等待用户停止操作后处理(如搜索、保存状态),用
setTimeout防抖。 - 如果你要在屏幕上实时响应选中变化(如浮动工具栏跟随选中文字),用
requestAnimationFrame节流,能避免布局抖动且更省电。
window.addEventListener 和 document.addEventListener 区别
window.addEventListener 和 document.addEventListener 在语法上完全一样(都继承自 EventTarget),但它们的监听对象和事件触发时机有本质区别。在日常开发中,如果混用,最容易踩的坑是滚动事件不生效和事件触发顺序不符合预期。
以下是核心区别和实战避坑指南:
1. 事件触发的“物理”路径(最关键)
document:代表 HTML 文档对象(DOM 树根节点)。window:代表包含文档的浏览器窗口(视口)。
当一个事件(如点击)发生时,传播路径如下(冒泡阶段):
目标元素 → <body> → <html> → document → window
因此,document 先于 window 收到冒泡事件。如果在捕获阶段(useCapture: true),则顺序完全相反(window 先于 document)。
2. 各自“专属”的事件类型(80% 的选错原因)
不同的对象负责不同的职责,以下事件只能在特定对象上稳定监听:
| 对象 | 专属/推荐监听的事件 | 备注 |
|---|---|---|
window | resize(窗口大小变化)、scroll(滚动条滚动)、hashchange(URL哈希变化)、online/offline(网络状态) | 注意:页面滚动条属于窗口视口,强烈推荐用 window 监听滚动,在 document 上监听滚动在移动端或某些浏览器(如Safari)会失效。 |
document | DOMContentLoaded(DOM解析完成)、visibilitychange(页面可见性变化)、keydown/keyup(全局键盘事件) | DOMContentLoaded 是 document 的专有事件,window 上监听不到。 |
3. this 指向(即 event.currentTarget)
- 监听器中的
this指向调用者:window监听器中的this指向Window对象;document监听器中的this指向Document对象。 - 建议:在回调中尽量使用
event.currentTarget而不是this,避免箭头函数改变上下文时产生混淆。
4. 加载时机的微妙区别
window.onload(或window.addEventListener('load')):必须等所有资源(图片、样式、脚本、字体)完全加载完毕后触发。document.addEventListener('DOMContentLoaded'):只需 DOM 树构建完成即可触发,无需等待图片等外部资源。如果你的 JS 需要操作 DOM,用这个速度更快,用户体验更好。
实战避坑:监听页面滚动的正确姿势
很多新手会写成 document.addEventListener('scroll', fn),结果在 iPhone 的 Safari 上毫无反应。
✅ 正确写法(兼容全平台):
// 必须绑定在 window 上
window.addEventListener('scroll', function(e) {
// 获取滚动高度用 document.documentElement.scrollTop
console.log('滚动位置:', document.documentElement.scrollTop);
});如果同时监听同一个事件(如 click),谁先触发?
假设在捕获阶段(第三个参数为 true):
window先触发(从外向内捕获)。
假设在冒泡阶段(第三个参数为 false 或默认):
document先触发,然后冒泡到window。
日常经验法则:
- 操作浏览器窗口本身(大小、滚动、标签页切换)→ 用
window。 - 操作页面 DOM 结构(节点加载、全局点击委托)→ 用
document。 - 除非特殊需求,不要把
click同时挂在两者上,容易造成事件重复处理或逻辑混乱。如果必须用,确保调用e.stopPropagation()阻断传播。
链接到当前文件 0
没有文件链接到当前文件