【Web APIs-Day2】事件监听与事件对象
📺 对应视频:P94-P104 | 🎯 核心目标:掌握事件绑定三种方式、常见事件类型、event对象及回调函数中的this
一、事件基础
1.1 什么是事件?
事件是用户或浏览器触发的行为(点击、输入、滚动等),JS 通过监听这些行为来响应。
事件三要素:
1. 事件源(谁触发):button、input、div...
2. 事件类型(什么行为):click、input、scroll...
3. 事件处理函数(做什么):() => { ... }
1.2 三种事件绑定方式
// 方式一:HTML 属性(行内,不推荐,耦合)
<button onclick="alert('clicked')">点我</button>
// 方式二:DOM0 级(赋值给 on* 属性)
const btn = document.querySelector('button')
btn.onclick = function() {
console.log('clicked')
}
// ⚠️ 缺点:只能绑定一个,后绑定的会覆盖前面的
btn.onclick = function() {
console.log('我覆盖了上面的') // 只有这个生效
}
// 方式三:addEventListener(推荐!)
btn.addEventListener('click', function() {
console.log('handler 1')
})
btn.addEventListener('click', function() {
console.log('handler 2') // 两个都会执行!
})
二、常见事件类型
2.1 鼠标事件
const box = document.querySelector('.box')
box.addEventListener('click', e => console.log('单击'))
box.addEventListener('dblclick', e => console.log('双击'))
box.addEventListener('mousedown', e => console.log('鼠标按下'))
box.addEventListener('mouseup', e => console.log('鼠标抬起'))
box.addEventListener('mousemove', e => {
console.log(`鼠标位置:${e.clientX}, ${e.clientY}`)
})
box.addEventListener('mouseenter', e => console.log('鼠标进入')) // 不冒泡
box.addEventListener('mouseleave', e => console.log('鼠标离开')) // 不冒泡
box.addEventListener('mouseover', e => console.log('鼠标悬停')) // 冒泡
box.addEventListener('mouseout', e => console.log('鼠标移出')) // 冒泡
box.addEventListener('contextmenu', e => {
e.preventDefault() // 禁止右键菜单
console.log('右键点击')
})
2.2 键盘事件
document.addEventListener('keydown', e => {
console.log('按下的键:', e.key) // 'Enter', 'ArrowUp', 'a'...
console.log('键码(旧):', e.keyCode) // 数字码(已废弃,但仍常见)
// 组合键
if (e.ctrlKey && e.key === 's') {
e.preventDefault() // 阻止浏览器默认保存
console.log('Ctrl+S 保存')
}
})
document.addEventListener('keyup', e => {
console.log('松开:', e.key)
})
// input 上的键盘事件
const input = document.querySelector('input')
input.addEventListener('keydown', e => {
if (e.key === 'Enter') {
console.log('用户按了回车,提交:', input.value)
}
})
常用 e.key 值:
'Enter' 回车
'Backspace' 退格
'Delete' 删除
'Escape' ESC
'ArrowUp' 上箭头
'ArrowDown' 下箭头
'Tab' Tab键
' ' 空格
2.3 焦点事件
const input = document.querySelector('input')
input.addEventListener('focus', e => {
console.log('获得焦点')
input.parentElement.classList.add('active') // 边框高亮
})
input.addEventListener('blur', e => {
console.log('失去焦点')
input.parentElement.classList.remove('active')
// 常用于:失焦时验证输入
if (input.value.length < 6) {
console.log('密码太短')
}
})
2.4 表单事件
const input = document.querySelector('input')
const form = document.querySelector('form')
// input:内容每次变化触发(实时)
input.addEventListener('input', e => {
console.log('当前值:', e.target.value)
// 实时显示字数
charCount.textContent = `${e.target.value.length}/100`
})
// change:失焦且内容改变时触发
input.addEventListener('change', e => {
console.log('变化后的值:', e.target.value)
})
// select:文本被选中时触发
input.addEventListener('select', e => {
console.log('选中了文本')
})
// submit:表单提交时触发
form.addEventListener('submit', e => {
e.preventDefault() // 阻止默认提交(页面刷新)
const data = new FormData(form) // 获取表单数据
// 然后用 fetch/axios 手动提交
})
三、事件对象(event)
3.1 event 对象是什么?
事件触发时,浏览器会自动创建一个事件对象(Event Object) ,包含该事件的所有信息,作为回调函数的第一个参数传入。
document.querySelector('button').addEventListener('click', function(event) {
// event 就是事件对象(习惯命名为 e 或 evt)
console.log(event) // 完整的事件对象
console.log(event.type) // 'click'(事件类型)
console.log(event.target) // 触发事件的元素
})
3.2 常用属性
element.addEventListener('click', e => {
// 目标元素
e.target // 实际触发事件的元素(可能是子元素)
e.currentTarget // 绑定事件监听的元素(= this)
// 事件类型
e.type // 'click'
// 鼠标位置
e.clientX / e.clientY // 相对于视口的位置
e.pageX / e.pageY // 相对于整个文档(含滚动)
e.offsetX / e.offsetY // 相对于元素本身
e.screenX / e.screenY // 相对于屏幕
// 键盘修饰键
e.altKey // Alt 键是否按下
e.ctrlKey // Ctrl 键是否按下
e.shiftKey // Shift 键是否按下
e.metaKey // Win/Command 键是否按下
})
3.3 事件方法
element.addEventListener('click', e => {
e.preventDefault() // 阻止默认行为(阻止链接跳转、表单提交等)
e.stopPropagation() // 停止事件冒泡(不向上传播)
})
// 常见应用
document.querySelector('a').addEventListener('click', e => {
e.preventDefault() // 阻止链接跳转
console.log('链接被点击,但没有跳转')
})
四、this 在事件回调中的指向
const btn = document.querySelector('button')
// 普通函数:this 指向事件源(绑定监听的元素)
btn.addEventListener('click', function() {
console.log(this) // <button>元素
console.log(this === btn) // true
this.classList.toggle('active') // 可以用 this 操作元素
})
// 箭头函数:this 继承外层,不是事件源!
btn.addEventListener('click', () => {
console.log(this) // Window(或严格模式下 undefined)
// ⚠️ 箭头函数不适合需要 this 的事件回调
})
// 推荐方案:用 e.currentTarget 替代 this,不受箭头函数影响
btn.addEventListener('click', (e) => {
e.currentTarget.classList.toggle('active') // ✅ 安全
})
五、removeEventListener(移除事件)
// 必须使用具名函数才能移除
function handleClick() {
console.log('clicked')
}
btn.addEventListener('click', handleClick) // 绑定
btn.removeEventListener('click', handleClick) // 移除
// ❌ 匿名函数无法移除(每次传入都是新函数)
btn.addEventListener('click', () => console.log('click'))
btn.removeEventListener('click', () => console.log('click')) // 无效!
// 实际应用:只触发一次
function handleOnce(e) {
console.log('只执行一次')
e.currentTarget.removeEventListener('click', handleOnce)
}
btn.addEventListener('click', handleOnce)
// 更简便的方式:options 参数中 once: true
btn.addEventListener('click', () => {
console.log('只触发一次')
}, { once: true }) // ✅ 触发一次后自动移除
六、综合案例
案例:实时搜索提示
const searchInput = document.querySelector('#search')
const resultList = document.querySelector('#results')
const data = ['JavaScript', 'Java', 'Python', 'PHP', 'C++', 'Go']
searchInput.addEventListener('input', e => {
const query = e.target.value.trim().toLowerCase()
if (!query) {
resultList.innerHTML = ''
return
}
const matches = data.filter(item => item.toLowerCase().includes(query))
resultList.innerHTML = matches
.map(item => `<li>${item}</li>`)
.join('')
})
// 按 ESC 清空
searchInput.addEventListener('keydown', e => {
if (e.key === 'Escape') {
searchInput.value = ''
resultList.innerHTML = ''
}
})
案例:跟随鼠标移动的元素
const dot = document.querySelector('.dot')
document.addEventListener('mousemove', e => {
dot.style.left = e.pageX + 'px'
dot.style.top = e.pageY + 'px'
})
七、知识图谱
事件监听与事件对象
├── 事件绑定
│ ├── HTML 属性 onclick=(不推荐)
│ ├── DOM0:el.onclick = fn(覆盖)
│ └── addEventListener(推荐,可多个)
├── 常见事件
│ ├── 鼠标:click/dblclick/mousemove/enter/leave
│ ├── 键盘:keydown/keyup(e.key 获取键名)
│ ├── 焦点:focus/blur
│ └── 表单:input(实时)/change(失焦)/submit
├── 事件对象 event
│ ├── e.target(触发源)
│ ├── e.type(事件类型)
│ ├── e.clientX/Y(鼠标位置)
│ ├── e.preventDefault()(阻止默认)
│ └── e.stopPropagation()(阻止冒泡)
└── this 与 event.currentTarget
├── 普通函数:this = 绑定元素
└── 箭头函数:this 继承外层(用 e.currentTarget 替代)
八、高频面试题
Q1:如何阻止表单提交?
form.addEventListener('submit', e => {
e.preventDefault() // 阻止默认提交行为
// 进行自定义验证和提交
})
Q2: e.target 和 e.currentTarget 的区别?
e.target是实际触发事件的元素(如子元素);e.currentTarget是绑定事件监听器的元素。在事件委托中,两者不同:父元素绑定监听,子元素触发,target是子,currentTarget是父。
Q3:如何给动态创建的元素绑定事件?
动态创建的元素在创建前就绑定不到,推荐用事件委托:把事件绑定在父元素上,通过
e.target判断是否是目标子元素。(下一篇详细讲)
⬅️ 上一篇:Web APIs Day1 - DOM操作基础 ➡️ 下一篇:Web APIs Day3 - 事件进阶与页面交互