事件是 JavaScript 与 HTML 交互的基础,允许页面响应用户操作和浏览器状态变化。
事件流描述了页面中接收事件的顺序。
<!-- HTML 结构 -->
<div id="outer">
<div id="inner">
<button id="button">Click me</button>
</div>
</div>// 事件冒泡:从最具体的元素开始,向上传播
const outer = document.getElementById('outer')
const inner = document.getElementById('inner')
const button = document.getElementById('button')
button.addEventListener('click', () => console.log('Button clicked'))
inner.addEventListener('click', () => console.log('Inner div clicked'))
outer.addEventListener('click', () => console.log('Outer div clicked'))
// 点击按钮输出:
// Button clicked
// Inner div clicked
// Outer div clicked// 事件捕获:从最外层元素开始,向下传播
button.addEventListener('click', () => console.log('Button clicked'), true)
inner.addEventListener('click', () => console.log('Inner div clicked'), true)
outer.addEventListener('click', () => console.log('Outer div clicked'), true)
// 点击按钮输出:
// Outer div clicked
// Inner div clicked
// Button clickedDOM2 Events 规范规定事件流包含三个阶段:
// 同时监听捕获和冒泡
button.addEventListener('click', () => console.log('Button - capture'), true)
button.addEventListener('click', () => console.log('Button - bubble'), false)
inner.addEventListener('click', () => console.log('Inner - capture'), true)
inner.addEventListener('click', () => console.log('Inner - bubble'), false)
// 点击按钮输出:
// Inner - capture
// Button - capture
// Button - bubble
// Inner - bubble添加事件处理程序的方法。
<!-- 不推荐:HTML 内联事件处理程序 -->
<button onclick="alert('Hello')">Click me</button>
<!-- 访问元素和事件对象 -->
<button onclick="console.log(this.tagName); console.log(event.type)">Click me</button>const button = document.getElementById('button')
// 添加事件处理程序
button.onclick = function(event) {
console.log('Button clicked')
console.log(this === button) // true
}
// 移除事件处理程序
button.onclick = nullconst button = document.getElementById('button')
// 添加事件处理程序
function handleClick(event) {
console.log('Button clicked')
console.log(this === button) // true
}
button.addEventListener('click', handleClick, false)
// 添加多个处理程序
button.addEventListener('click', function() {
console.log('Another handler')
}, false)
// 移除事件处理程序
button.removeEventListener('click', handleClick, false)
// 一次性事件处理程序
button.addEventListener('click', function handler() {
console.log('This will only run once')
button.removeEventListener('click', handler)
})事件触发时会创建 event 对象,包含事件相关信息。
button.addEventListener('click', function(event) {
// 事件基本信息
console.log(event.type) // 事件类型,如 'click'
console.log(event.target) // 事件目标
console.log(event.currentTarget) // 当前事件处理程序绑定的元素
// 鼠标事件位置
console.log(event.clientX, event.clientY) // 视口坐标
console.log(event.pageX, event.pageY) // 页面坐标
console.log(event.screenX, event.screenY) // 屏幕坐标
// 键盘事件
console.log(event.key) // 按键值
console.log(event.keyCode) // 按键码(已废弃)
console.log(event.code) // 按键代码
// 修饰键
console.log(event.ctrlKey) // Ctrl 键是否按下
console.log(event.altKey) // Alt 键是否按下
console.log(event.shiftKey) // Shift 键是否按下
console.log(event.metaKey) // Meta 键是否按下(Cmd 或 Windows 键)
})button.addEventListener('click', function(event) {
// 阻止默认行为
event.preventDefault()
// 停止事件传播
event.stopPropagation()
// 立即停止事件传播(包括同一元素其他处理程序)
event.stopImmediatePropagation()
})// 加载事件
window.addEventListener('load', () => {
console.log('Page loaded')
})
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM ready')
})
// 卸载事件
window.addEventListener('beforeunload', (event) => {
event.returnValue = 'Are you sure you want to leave?'
})
window.addEventListener('unload', () => {
console.log('Page unloading')
})
// 调整大小事件
window.addEventListener('resize', () => {
console.log(`Window size: ${window.innerWidth}x${window.innerHeight}`)
})
// 滚动事件
window.addEventListener('scroll', () => {
console.log(`Scroll position: ${window.pageXOffset}, ${window.pageYOffset}`)
})const element = document.getElementById('element')
// 基本鼠标事件
element.addEventListener('click', handleClick)
element.addEventListener('dblclick', handleDoubleClick)
element.addEventListener('mousedown', handleMouseDown)
element.addEventListener('mouseup', handleMouseUp)
element.addEventListener('mousemove', handleMouseMove)
// 鼠标按键信息
function handleMouseDown(event) {
switch (event.button) {
case 0:
console.log('Left button')
break
case 1:
console.log('Middle button')
break
case 2:
console.log('Right button')
break
}
}
// 滚轮事件
element.addEventListener('wheel', (event) => {
console.log(`Delta: ${event.deltaX}, ${event.deltaY}, ${event.deltaZ}`)
console.log(`Mode: ${event.deltaMode}`) // 0: 像素, 1: 行, 2: 页
})const input = document.getElementById('input')
// 键盘事件
input.addEventListener('keydown', (event) => {
console.log(`Key down: ${event.key}, code: ${event.code}`)
})
input.addEventListener('keypress', (event) => {
console.log(`Key press: ${event.key}`)
})
input.addEventListener('keyup', (event) => {
console.log(`Key up: ${event.key}`)
})
// 组合键检测
input.addEventListener('keydown', (event) => {
if (event.ctrlKey && event.key === 's') {
event.preventDefault()
console.log('Ctrl+S pressed - saving...')
}
})const form = document.getElementById('form')
const input = document.getElementById('input')
// 表单事件
form.addEventListener('submit', (event) => {
event.preventDefault()
console.log('Form submitted')
})
form.addEventListener('reset', () => {
console.log('Form reset')
})
// 输入事件
input.addEventListener('focus', () => {
console.log('Input focused')
})
input.addEventListener('blur', () => {
console.log('Input blurred')
})
input.addEventListener('change', () => {
console.log('Input value changed')
})
input.addEventListener('input', () => {
console.log('Input value changing')
})const textArea = document.getElementById('textArea')
textArea.addEventListener('copy', (event) => {
console.log('Content copied')
// 可以修改剪贴板内容
event.clipboardData.setData('text/plain', 'Modified text')
event.preventDefault()
})
textArea.addEventListener('paste', (event) => {
console.log('Content pasted')
const pastedText = event.clipboardData.getData('text/plain')
console.log('Pasted:', pastedText)
})
textArea.addEventListener('cut', () => {
console.log('Content cut')
})利用事件冒泡,将事件处理程序添加到祖先元素。
// 不推荐:为每个列表项添加事件处理程序
const items = document.querySelectorAll('.item')
items.forEach(item => {
item.addEventListener('click', handleItemClick)
})
// 推荐:事件委托
const list = document.getElementById('list')
list.addEventListener('click', (event) => {
if (event.target.matches('.item')) {
handleItemClick(event)
}
})
// 更复杂的委托
list.addEventListener('click', (event) => {
const target = event.target
if (target.matches('.delete-btn')) {
deleteItem(target.dataset.id)
} else if (target.matches('.edit-btn')) {
editItem(target.dataset.id)
} else if (target.matches('.item')) {
selectItem(target.dataset.id)
}
})创建和触发自定义事件。
// 创建自定义事件
const customEvent = new CustomEvent('myEvent', {
detail: { message: 'Hello from custom event' },
bubbles: true, // 是否冒泡
cancelable: true // 是否可以取消
})
// 监听自定义事件
element.addEventListener('myEvent', (event) => {
console.log(event.detail.message)
})
// 触发自定义事件
element.dispatchEvent(customEvent)
// 兼容性处理
function createCustomEvent(eventName, detail) {
let event
if (typeof CustomEvent === 'function') {
event = new CustomEvent(eventName, { detail })
} else {
event = document.createEvent('CustomEvent')
event.initCustomEvent(eventName, true, true, detail)
}
return event
}// 在不需要时移除事件处理程序
const button = document.createElement('button')
const handler = () => console.log('Clicked')
button.addEventListener('click', handler)
// 移除事件处理程序
button.removeEventListener('click', handler)
// 在元素被移除前移除事件处理程序
function removeElement(element) {
// 移除所有事件处理程序
element.parentNode.removeChild(element)
}// 节流:限制函数执行频率
function throttle(func, delay) {
let lastCall = 0
return function(...args) {
const now = Date.now()
if (now - lastCall >= delay) {
lastCall = now
func.apply(this, args)
}
}
}
// 防抖:延迟执行,直到停止触发
function debounce(func, delay) {
let timeoutId
return function(...args) {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => func.apply(this, args), delay)
}
}
// 使用示例
window.addEventListener('resize', debounce(handleResize, 250))
window.addEventListener('scroll', throttle(handleScroll, 16))事件是 JavaScript 编程的核心概念:
合理使用事件可以创建丰富的用户交互体验。