在事件处理中,我们经常需要访问原生 DOM 事件对象:获取鼠标位置、读取键盘按键、阻止默认行为...Vue 提供了多种方式来访问这个事件对象。
当 DOM 事件触发时,浏览器会创建一个事件对象,包含事件的详细信息:
{
type: 'click', // 事件类型
target: element, // 触发事件的元素
currentTarget: element, // 绑定事件的元素
clientX: 100, // 鼠标 X 坐标
clientY: 200, // 鼠标 Y 坐标
key: 'Enter', // 按键值
keyCode: 13, // 按键码
preventDefault: fn, // 阻止默认行为
stopPropagation: fn // 阻止冒泡
}
当事件处理器没有参数时,Vue 会自动将事件对象作为第一个参数传入:
<button @click="handleClick">点击</button>
<script>
methods: {
handleClick: function(event) {
console.log(event.type) // 'click'
console.log(event.target) // <button> 元素
console.log(event.clientX) // 鼠标 X 坐标
}
}
</script>
这是最简单的方式,适用于不需要传递额外参数的场景。
当需要同时传递自定义参数和事件对象时,使用 $event 变量:
<button @click="handle('按钮', $event)">点击</button>
<script>
methods: {
handle: function(message, event) {
console.log(message) // '按钮'
console.log(event.type) // 'click'
console.log(event.target) // <button> 元素
}
}
</script>
$event 是 Vue 提供的特殊变量,代表原生 DOM 事件对象。
也可以使用箭头函数来传递参数:
<button @click="(e) => handleClick(item, e)">点击</button>
<script>
methods: {
handleClick: function(item, event) {
console.log(item.name)
console.log(event.target)
}
}
</script>
这种方式更灵活,但代码稍长。
<div
@click="handleClick"
@mousemove="handleMouseMove"
@mousedown="handleMouseDown"
@mouseup="handleMouseUp"
@dblclick="handleDoubleClick"
@contextmenu="handleRightClick"
>
鼠标操作区域
</div>
<script>
methods: {
handleClick: function(event) {
// 鼠标点击位置
console.log('X:', event.clientX)
console.log('Y:', event.clientY)
// 相对于触发元素的坐标
console.log('offsetX:', event.offsetX)
console.log('offsetY:', event.offsetY)
// 相对于页面的坐标
console.log('pageX:', event.pageX)
console.log('pageY:', event.pageY)
// 相对于屏幕的坐标
console.log('screenX:', event.screenX)
console.log('screenY:', event.screenY)
// 按键信息
console.log('左键:', event.button === 0)
console.log('中键:', event.button === 1)
console.log('右键:', event.button === 2)
// 修饰键状态
console.log('Ctrl:', event.ctrlKey)
console.log('Shift:', event.shiftKey)
console.log('Alt:', event.altKey)
console.log('Meta:', event.metaKey)
},
handleMouseMove: function(event) {
// 实时获取鼠标位置
this.mouseX = event.clientX
this.mouseY = event.clientY
},
handleRightClick: function(event) {
event.preventDefault() // 阻止默认右键菜单
// 显示自定义菜单
this.showContextMenu(event.clientX, event.clientY)
}
}
</script>
<input
@keydown="handleKeyDown"
@keyup="handleKeyUp"
placeholder="按下键盘"
>
<script>
methods: {
handleKeyDown: function(event) {
// 按键值(推荐)
console.log('key:', event.key) // 'Enter', 'a', '1', 'ArrowUp'
console.log('code:', event.code) // 'Enter', 'KeyA', 'Digit1', 'ArrowUp'
// 按键码(已废弃,但仍可用)
console.log('keyCode:', event.keyCode)
// 修饰键状态
console.log('Ctrl:', event.ctrlKey)
console.log('Shift:', event.shiftKey)
console.log('Alt:', event.altKey)
// 阻止默认行为
if (event.key === 'Enter') {
event.preventDefault()
this.submit()
}
}
}
</script>
<form @submit="handleSubmit">
<input type="text" @input="handleInput" @change="handleChange">
<input type="checkbox" @change="handleCheck">
<select @change="handleSelect">
<option value="1">选项一</option>
<option value="2">选项二</option>
</select>
</form>
<script>
methods: {
handleSubmit: function(event) {
event.preventDefault() // 阻止表单默认提交
console.log('表单提交')
},
handleInput: function(event) {
// 实时获取输入值
console.log(event.target.value)
},
handleChange: function(event) {
// 值改变时触发
console.log('新值:', event.target.value)
console.log('checked:', event.target.checked) // 复选框
},
handleSelect: function(event) {
// 下拉选择
console.log('选中:', event.target.value)
}
}
</script>
这两个属性经常混淆,区别在于:
event.target:触发事件的元素(可能是子元素)event.currentTarget:绑定事件的元素(始终是当前元素)<div class="outer" @click="handleClick">
<button class="inner">按钮</button>
</div>
<script>
methods: {
handleClick: function(event) {
console.log('target:', event.target) // <button> 元素
console.log('currentTarget:', event.currentTarget) // <div> 元素
// 判断是否点击的是绑定事件的元素本身
if (event.target === event.currentTarget) {
console.log('点击的是外层 div')
}
}
}
</script>
<div
class="draggable"
:style="{ left: x + 'px', top: y + 'px' }"
@mousedown="startDrag"
>
拖拽我
</div>
<script>
new Vue({
data: {
isDragging: false,
x: 100,
y: 100,
startX: 0,
startY: 0
},
methods: {
startDrag: function(event) {
this.isDragging = true
this.startX = event.clientX - this.x
this.startY = event.clientY - this.y
document.addEventListener('mousemove', this.drag)
document.addEventListener('mouseup', this.stopDrag)
},
drag: function(event) {
if (!this.isDragging) return
this.x = event.clientX - this.startX
this.y = event.clientY - this.startY
},
stopDrag: function() {
this.isDragging = false
document.removeEventListener('mousemove', this.drag)
document.removeEventListener('mouseup', this.stopDrag)
}
}
})
</script>
<div @contextmenu.prevent="showMenu">
右键点击显示菜单
</div>
<div
v-if="menuVisible"
class="context-menu"
:style="{ left: menuX + 'px', top: menuY + 'px' }"
>
<div @click="copy">复制</div>
<div @click="paste">粘贴</div>
<div @click="delete">删除</div>
</div>
<script>
new Vue({
data: {
menuVisible: false,
menuX: 0,
menuY: 0
},
methods: {
showMenu: function(event) {
this.menuX = event.clientX
this.menuY = event.clientY
this.menuVisible = true
},
copy: function() {
console.log('复制')
this.menuVisible = false
},
paste: function() {
console.log('粘贴')
this.menuVisible = false
},
delete: function() {
console.log('删除')
this.menuVisible = false
}
},
mounted: function() {
// 点击其他地方关闭菜单
document.addEventListener('click', this.hideMenu)
},
beforeDestroy: function() {
document.removeEventListener('click', this.hideMenu)
},
methods: {
hideMenu: function() {
this.menuVisible = false
}
}
})
</script>
<div class="container" @mousemove="followMouse">
<div class="follower" :style="followerStyle"></div>
</div>
<script>
new Vue({
data: {
mouseX: 0,
mouseY: 0
},
computed: {
followerStyle: function() {
return {
transform: `translate(${this.mouseX}px, ${this.mouseY}px)`
}
}
},
methods: {
followMouse: function(event) {
// 相对于容器的坐标
const rect = event.currentTarget.getBoundingClientRect()
this.mouseX = event.clientX - rect.left
this.mouseY = event.clientY - rect.top
}
}
})
</script>
<ul>
<li
v-for="item in items"
:key="item.id"
@click="selectItem(item, $event)"
>
{{ item.name }}
</li>
</ul>
<script>
methods: {
selectItem: function(item, event) {
// 获取触发元素
const element = event.target
// 获取自定义属性
const id = element.dataset.id
// 获取元素尺寸
console.log('宽度:', element.offsetWidth)
console.log('高度:', element.offsetHeight)
// 获取元素位置
const rect = element.getBoundingClientRect()
console.log('位置:', rect.left, rect.top)
// 添加样式
element.classList.add('selected')
}
}
</script>
Vue 提供的事件修饰符和事件对象方法可以互换使用:
<!-- 使用修饰符 -->
<form @submit.prevent="handleSubmit">
<button @click.stop="handleClick">
<!-- 使用事件对象方法 -->
<form @submit="handleSubmit">
<button @click="handleClick">
<script>
methods: {
handleSubmit: function(event) {
event.preventDefault()
},
handleClick: function(event) {
event.stopPropagation()
}
}
</script>
推荐使用修饰符
修饰符让意图更清晰,代码更简洁。但在某些复杂场景下,可能需要在方法中手动调用事件对象方法。
事件对象是处理复杂交互的关键:
target、clientX/Y、key、keyCode 等掌握事件对象的使用,能让你处理各种复杂的交互场景,从简单的点击到复杂的拖拽、绘图等功能。