本文系统梳理了 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"。