本文系统梳理了 Web 中的鼠标、键盘与指针事件机制,包括事件触发顺序、事件属性、组合键判断、坐标获取、防止默认行为,以及拖放操作与 Pointer Events 的使用。同时,解析了 event.key 与 event.code 的区别,帮助开发者更精准地处理 UI 交互逻辑。
鼠标事件
事件顺序
从上面的列表中我们可以看到,一个用户操作可能会触发多个事件。
例如,点击鼠标左键,在鼠标左键被按下时,会首先触发
mousedown
,然后当鼠标左键被释放时,会触发
mouseup
和 click
。
在单个动作触发多个事件时,事件的顺序是固定的。也就是说,会遵循
mousedown
→ mouseup
→ click
的顺序调用处理程序。
鼠标按钮
与点击相关的事件始终具有 button
属性,该属性允许获取确切的鼠标按钮。
通常我们不在 click
和 contextmenu
事件中使用这一属性,因为前者只在单击鼠标左键时触发,后者只在单击鼠标右键时触发。
不过,在 mousedown
和 mouseup
事件中则可能需要用到
event.button
,因为这两个事件在任何按键上都会触发,所以我们可以使用
button
属性来区分是左键单击还是右键单击。
event.button
的所有可能值如下:
鼠标按键状态 | event.button |
---|---|
左键 (主要按键) | 0 |
中键 (辅助按键) | 1 |
右键 (次要按键) | 2 |
X1 键 (后退按键) | 3 |
X2 键 (前进按键) | 4 |
大多数鼠标设备只有左键和右键,对应的值就是 0
和
2
。触屏设备中的点按操作也会触发类似的事件。
组合键:shift,alt,ctrl,meta
所有的鼠标事件都包含有关按下的组合键的信息。
shiftKey
:ShiftaltKey
:Alt(或对于 Mac 是 Opt)ctrlKey
:CtrlmetaKey
:对于 Mac 是 Cmd
如果在事件期间按下了相应的键,则它们为 true
。
比如,下面这个按钮仅在 Alt+Shift+click 时才有效:
1 | <button id="button">Alt+Shift+Click on me!</button> |
坐标:clientX/Y,pageX/Y
所有的鼠标事件都提供了两种形式的坐标:
- 相对于窗口的坐标:
clientX
和clientY
。 - 相对于文档的坐标:
pageX
和pageY
。
防止在鼠标按下时的选择
双击鼠标会有副作用,在某些界面中可能会出现干扰:它会选择文本。
比如,双击下面的文本,除了我们的处理程序外,还会选择文本:
1 | <span ondblclick="alert('dblclick')">Double-click me</span> |
如果按下鼠标左键,并在不松开的情况下移动鼠标,这也常常会造成不必要的选择。
在这种情况下,最合理的方式是防止浏览器对 mousedown
进行操作。这样能够阻止刚刚提到的两种选择:
1 | Before... |
如果我们想禁用选择以保护我们页面的内容不被复制粘贴,那么我们可以使用另一个事件:
oncopy
。
1
2
3
4 <div oncopy="alert('Copying forbidden!');return false">
Dear user, The copying is forbidden for you. If you know JS or HTML, then
you can get everything from the page source though.
</div>
移动鼠标:mouseover/out,mouseenter/leave
当鼠标指针移到某个元素上时,mouseover
事件就会发生,而当鼠标离开该元素时,mouseout
事件就会发生。
事件 mouseenter/mouseleave
类似于
mouseover/mouseout
。它们在鼠标指针进入/离开元素时触发。
但是有两个重要的区别:
- 元素内部与后代之间的转换不会产生影响。
- 事件
mouseenter/mouseleave
不会冒泡。
鼠标拖放事件
- 事件流:
ball.mousedown
→document.mousemove
→ball.mouseup
(不要忘记取消原生ondragstart
)。 - 在拖动开始时 ——
记住鼠标指针相对于元素的初始偏移(shift):
shiftX/shiftY
,并在拖动过程中保持它不变。 - 使用
document.elementFromPoint
检测鼠标指针下的 “droppable” 的元素。
我们可以在此基础上做很多事情。
- 在
mouseup
上,我们可以智能地完成放置(drop):更改数据,移动元素。 - 我们可以高亮我们正在“飞过”的元素。
- 我们可以将拖动限制在特定的区域或者方向。
- 我们可以对
mousedown/up
使用事件委托。一个大范围的用于检查event.target
的事件处理程序可以管理数百个元素的拖放。
指针事件
指针事件(Pointer Events)是一种用于处理来自各种输入设备(例如鼠标、触控笔和触摸屏等)的输入信息的现代化解决方案。
指针事件类型
指针事件 | 类似的鼠标事件 |
---|---|
pointerdown |
mousedown |
pointerup |
mouseup |
pointermove |
mousemove |
pointerover |
mouseover |
pointerout |
mouseout |
pointerenter |
mouseenter |
pointerleave |
mouseleave |
pointercancel |
- |
gotpointercapture |
- |
lostpointercapture |
- |
不难发现,每一个 mouse<event>
都有与之相对应的
pointer<event>
。
指针事件属性
指针事件具备和鼠标事件完全相同的属性,包括 clientX/Y
和
target
等,以及一些其他属性:
pointerId
—— 触发当前事件的指针唯一标识符。浏览器生成的。使我们能够处理多指针的情况,例如带有触控笔和多点触控功能的触摸屏(下文会有相关示例)。
pointerType
—— 指针的设备类型。必须为字符串,可以是:“mouse”、“pen” 或 “touch”。我们可以使用这个属性来针对不同类型的指针输入做出不同响应。
isPrimary
—— 当指针为首要指针(多点触控时按下的第一根手指)时为true
。
键盘:keydown 和 keyup
当一个按键被按下时,会触发 keydown
事件,而当按键被释放时,会触发 keyup
事件。
event.code 和 event.key
事件对象的 key
属性允许获取字符,而事件对象的
code
属性则允许获取“物理按键代码”。
例如,同一个按键 Z,可以与或不与 Shift
一起按下。我们会得到两个不同的字符:小写的 z
和大写的
Z
。
event.key
正是这个字符,并且它将是不同的。但是,event.code
是相同的:
Key | event.key |
event.code |
---|---|---|
Z | z (小写) |
KeyZ |
Shift+Z | Z (大写) |
KeyZ |
如果用户使用不同的语言,那么切换到另一种语言将产生完全不同的字符,而不是
"Z"
。它将成为 event.key
的值,而
event.code
则始终都是一样的:"KeyZ"
。