JavaScript高级程序设计笔记 17
第17章 事件
1. 事件流
- 事件冒泡(IE 提出):从最具体的元素(触发事件的元素)开始,逐级向上传播到较不具体的元素(直到 document,某些浏览器到 window)。
- 事件捕获(Netscape 提出):从最不具体的元素(document)开始,向下传播到最具体的元素。
- DOM2 Events 规范了三个阶段:捕获阶段 → 目标阶段 → 冒泡阶段。
- 实际开发中通常使用冒泡阶段(默认)。
- 捕获阶段用于特殊场景(如事件委托之前拦截)。
2. 事件处理程序(事件监听器)
2.1 HTML 事件处理程序(过时)
- 直接在 HTML 中定义:
<button onclick="alert('clicked')">。 - 缺点:HTML/JS 耦合;作用域链复杂;不同浏览器实现差异大。
2.2 DOM0 事件处理程序
- 赋值给元素的事件属性(如
onclick):let btn = document.getElementById('myBtn'); btn.onclick = function() { console.log(this.id); }; // this 指向当前元素 btn.onclick = null; // 移除事件 - 特点:简单,跨浏览器;只能绑定一个处理程序(后赋值的会覆盖)。
2.3 DOM2 事件处理程序
- 添加:
element.addEventListener(type, listener, options)。type:事件名(不含 'on')。listener:回调函数。options:可选布尔值(false表示冒泡阶段,true表示捕获阶段)或对象(capture、once、passive)。
- 移除:
element.removeEventListener(type, listener, options)(需要传入同一函数引用,匿名函数无法移除)。 - 优点:同一元素可以添加多个监听器,执行顺序按添加顺序。
2.4 IE 事件处理程序(仅旧 IE)
attachEvent(detachEvent),this指向 window,不推荐。
3. 事件对象(event)
3.1 DOM 事件对象(标准)
- 属性:
type:事件类型。target:实际触发事件的元素。currentTarget:绑定监听器的元素(this指向它)。eventPhase:当前阶段(1=捕获,2=目标,3=冒泡)。bubbles:是否冒泡。cancelable:是否可取消默认行为。defaultPrevented:是否已调用preventDefault()。detail:事件相关的额外细节(如点击次数)。
- 方法:
preventDefault():阻止默认行为(如链接跳转、表单提交)。stopPropagation():阻止事件进一步传播(冒泡或捕获)。stopImmediatePropagation():阻止同一事件的其他监听器被调用(且阻止传播)。trusted:true表示由用户操作触发,false表示由脚本触发。
3.2 IE 事件对象(仅旧 IE)
- 通过
window.event访问,属性与 DOM 事件对象有差异,但兼容方式可用event = event || window.event。
4. 事件类型(常用)
4.1 UI 事件
load:页面/资源加载完成。unload:页面卸载(跳转或关闭)。resize:窗口大小改变。scroll:滚动条滚动(在任意元素上)。
4.2 焦点事件
focus(不冒泡)、blur(不冒泡)。focusin(冒泡)、focusout(冒泡)。
4.3 鼠标与滚轮事件
- 鼠标事件:
click、dblclick、mousedown、mouseup、mousemove、mouseenter(不冒泡)、mouseleave(不冒泡)、mouseover、mouseout。 - 滚轮事件:
wheel(取代已废弃的mousewheel)。 - 鼠标位置属性(相对于视口):
clientX/clientY。 - 鼠标位置属性(相对于页面):
pageX/pageY(有滚动偏移)。 - 鼠标位置属性(相对于屏幕):
screenX/screenY。 - 相关元素:
relatedTarget(在mouseover和mouseout中记录进出方向)。 - 修饰键:
shiftKey、ctrlKey、altKey、metaKey。
4.4 键盘与输入事件
keydown:按下任意键(包括功能键)。keypress:已废弃(建议使用keydown或input)。keyup:抬起按键。textInput(非标准):在可编辑区域获得输入时触发(能获取实际输入字符)。- 属性:
key(字符)、code(物理按键)、location(按键位置)。
4.5 触摸与手势事件(移动端)
- 触摸事件:
touchstart、touchmove、touchend、touchcancel。 - 触摸点列表:
touches(所有触摸点)、targetTouches(目标元素上的触摸点)、changedTouches(本次变化的触摸点)。 - 手势事件(非标准,如 iOS 的
gesturestart):缩放/旋转。
4.6 进度事件
load、error、abort、progress(常用于XMLHttpRequest、img、script等)。
4.7 表单事件
submit、reset、change(值改变并失焦)、input(值实时变化)。focus/blur(表单字段)。
4.8 拖放事件(HTML5 拖拽)
- 拖拽元素:
dragstart、drag、dragend。 - 放置目标:
dragenter、dragover、dragleave、drop。
4.9 剪贴板事件
copy、cut、paste。- 可以访问或修改剪贴板数据(通过
clipboardData对象)。
4.10 其他事件
DOMContentLoaded:DOM 树构建完成(不等待图片等资源),比load早。beforeunload:页面即将卸载,可用于弹出“确认离开”对话框。hashchange:URL hash 变化。pageshow/pagehide:处理浏览器缓存(bfcache)情况。
5. 事件委托(Event Delegation)
- 原理:利用事件冒泡,在父元素上监听事件,通过
e.target判断触发元素。 - 优点:减少内存消耗(只需一个父级监听器);动态添加的子元素无需重新绑定。
- 示例:
document.getElementById('list').addEventListener('click', (e) => { if (e.target && e.target.matches('li.item')) { console.log('点击了', e.target.textContent); } });
6. 模拟事件
6.1 旧方法(DOM2)
document.createEvent(eventType)→ 初始化(如initMouseEvent) →dispatchEvent。
6.2 现代方法(DOM3/ES6)
- 直接使用构造函数:
new Event(type, options)、new MouseEvent(type, options)、new KeyboardEvent(type, options)等。 - 示例:
let clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true }); element.dispatchEvent(clickEvent);
7. 最佳实践
- 使用
addEventListener,避免 HTML 或 DOM0 方式。 - 移除无用监听器,尤其是在单页应用(SPA)中防止内存泄漏。
- 优先使用事件委托,减少监听器数量。
- 合理利用
once: true选项,使监听器自动移除。 - 尽量避免使用
mouseover/mouseout,改用mouseenter/mouseleave(不冒泡,性能好)。 - 对滚动等高频事件使用
passive: true提升流畅度。
8. 小结
- 事件流分三阶段:捕获→目标→冒泡。
- 绑定用
addEventListener,移除需同名函数。 - 事件对象有
target与currentTarget,preventDefault阻默认,stopPropagation停传播。 - 事件委托靠冒泡,省内存,动态元素照样跑。
9. 常见面试题速查
-
事件冒泡和事件捕获的区别?
→ 冒泡从目标向上到window,捕获从window向下到目标。 -
如何阻止事件冒泡?如何阻止默认行为?
→stopPropagation();preventDefault()。 -
target和currentTarget的区别?
→target是实际触发事件的元素;currentTarget是绑定监听器的元素(this指向它)。 -
事件委托的原理?优点?
→ 利用冒泡,在父元素监听,通过target判断。优点:减少监听器,支持动态子元素。 -
load事件与DOMContentLoaded事件的区别?
→DOMContentLoaded在 DOM 树构建完成后触发,不等资源;load等待所有资源(图片、样式等)加载完成。 -
鼠标事件中
clientX与pageX的区别?
→clientX相对于视口(不含滚动),pageX相对于页面(含滚动偏移)。 -
如何实现一个可拖拽的元素?
→ 监听mousedown、mousemove、mouseup,计算偏移量,修改top/left(或使用drag and dropAPI)。 -
addEventListener的第三个参数有哪些常用选项?
→capture(布尔,是否在捕获阶段)、once(只执行一次)、passive(不会调用preventDefault,提升滚动性能)。 -
如何模拟触发一个事件?
→ 使用new Event()或new MouseEvent()等创建事件对象,调用dispatchEvent()。 -
mouseenter与mouseover的区别?
→mouseenter不冒泡,进入子元素不会重复触发;mouseover会冒泡,进入子元素会再次触发。

浙公网安备 33010602011771号