最近在开发过程中遇到了一个问题,感觉可以通过监听keydown事件来解决。但是,在实践的过程中发现,我原先对于keydown事件的理解并不是很正确。所以,在详细了解过keydown事件之后,在此做个总结。
本文主要包括以下几个方面的内容:
- 遇到的问题
- keydown事件
- 解决方案
遇到的问题
在开发过程中,我遇到这样一个需求:
当鼠标悬浮在目标dom上的时候,鼠标需要设置成特殊的样式:
- 没有按住ctrl键,鼠标样式设置成样式1;
- 按住ctrl键,鼠标样式设置成样式2。
我的第一想法是,通过监听mousemove事件来判断鼠标是否悬浮在目标dom上。然后通过event.ctrlKey来判断此时是否按住了ctrl键:
- 如果没有按住,处理上述情况1;
- 如果按住了,处理上述情况2。
但是,只监听mousemove事件不能覆盖所有的情况。比如,首先把鼠标移动到目标dom上,此时鼠标不动,按下或者松开ctrl键,此时鼠标的样式是需要变化的。但是,我们在按下或者松开ctrl键之后并没有移动鼠标,所以不会触发mousemove事件。
所以,我就想通过keydown和keyup事件来监听ctrl键的按下和松开事件。
首先,我给目标dom添加了keydown事件。这时,问题就来了:keydown事件根本就没有触发。
然后,我就想试试在body上绑定keydown事件,然后通过判断event.target是否是目标dom来判断鼠标位置。
最后,我发现event.target是body元素,而不是我想要的目标dom。所以,我就开始查找资料,发现keydown事件原来和我想象中的不一样。
keydown事件
通过查阅MDN上的keydown事件,发现该事件有如下限制:
Keyboard events are only generated by <inputs>, <textarea> and anything with the contentEditable attribute or with tabindex="-1".
首先我的目标dom不是input
和textarea
元素,所以就考虑添加contentEditable
或者tabindex
属性。
首先,我们看下contentEditable属性。当把这个元素设置成可编辑之后(不可编辑态不会触发keydown事件),元素的内容就可以像textarea
一样进行编辑了,这个不是我要的功能,所以这种方式不行。
然后,可以设置tabindex="-1"
。此时,在一定条件下,是可以触发keydown事件的。这个一定条件就是,首先通过点击操作让目标dom处于focus的状态,此时再按下ctrl键才会触发keydown事件。显然,这种方式也不能满足需求。
此时,我们先思考一个问题:为什么在第一部分给body添加keydown事件是可以的。我们已知的信息是:
- keyboard事件触发的条件:特殊元素,contentEditable,tabindex="-1"。
所以,我怀疑body元素设置了tabindex属性:
document.body.tabIndex // -1
果然如此!
前面我们有说到,只有当dom处于focus状态下的时候,按下ctrl键才会触发keydown事件。那么,对于body元素是不是也是这样的呢?
首先,我们退出页面的全屏模式,然后点击不属于浏览器的地方。此时,浏览器页面处于失去焦点的状态。
我们把鼠标重新移动到页面的内容区域(注意,只是移动过去,不要点击)。然后,按下ctrl键,你会发现keydown事件同样没有触发。
但是,对于body元素来说,先触发focus事件并不是一个问题。在刚才页面失去焦点的情况下,把鼠标移动到页面上有hover效果的元素上,发现这些元素的hover效果同样不会触发。所以,页面操作的默认前提应该就是页面处于focus状态。这样对于body元素来说,keydown事件触发前首先进行focus操作就不是一个特殊的操作了。
解决方案
通过上述部分的内容,我们知道可以监听body元素的keydown事件。那么,接下来的问题就变成了怎么结合body的keydown事件来判断ctrl键按下或者松开的时候,鼠标是否正好在目标dom上。
我是通过监听document元素的mousemove事件来解决的。当mousemove事件触发的时候,把event.target保存在一个全局变量中。当body的keydown事件触发的时候,通过判断这个全局变量是否是目标dom来控制后续逻辑。ctrl键松开的逻辑同理,通过body的keyup事件来监听。
总结
希望大家能有所收获。如有错误,欢迎留言讨论。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。