背景
最近在开发一个WebVNC组件,组件需要监听键盘事件,将键盘输入内容输入到VNC里。当使用组件元素DIV.addEventListener('keypress', func)时,却无法监听。尝试直接监听document,可以实现功能。但当我把组件放到页面里时又遇到了些问题:如果页面中包含一些表单元素,比如Input,如果我想在Input中输入内容,这些内容也会被WebVNC组件监听并输入到组件中,这显然不是想要的效果。
为什么组件无法监听键盘事件
组件跟页面Input输入冲突的原因,是因为我用了document来监听keypress。那为什么组件无法监听键盘事件呢?于是求助谷歌娘,了解到,只有可聚焦元素聚焦后,才能监听到键盘事件。
什么是可聚焦元素
可聚焦元素(Focusable Elements),顾名思义,就是可以聚焦(focus)的元素(=。=)。常用的,比如Input、Textarea、Button都属于可聚焦元素。除了键盘事件,当然也可以监听或触发focus跟blur。
原来,因为我的组件容器使用的是div,而它并不属于可聚焦元素,因而无法绑定键盘事件。原因知道了,那怎么解决呢?div换成input是不太可能了,那能不能把它变成可聚焦元素呢?
创建可聚焦元素
嘿嘿,还真可以!通过这个表格可以看到,虽然div不属于可聚焦元素,但如果加上一个tabindex就可以啦!
为了验证效果,可以使用document.activeElement来查询当前页面上被聚焦的元素。
注意:一个页面中,被聚焦的只会有一个元素,默认被聚焦的是body。
<div id="block1" class="block">没加tabindex的block1</div>
<div id="block2" class="block" tabindex="1">加了tabindex的block2</div>
如图,点击block2后会被添加上默认的outline样式:
其他方法
以上添加tabindex的方法,应该是最快捷的了。当然,除此之外,也有其他方式:
使用iframe
使用iframe相当于拥有了一个独立的document,键盘事件自然不会产生冲突。同时,iframe元素本身也是属于可聚焦元素。不过,使用该方法,就要求为每个WebVNC实例创建一个独立地址,在前后端的设计上,就要做一定修改了。
手动创建/移除document上的键盘事件
给组件模拟一个focus的状态,当点击组件区域时,更新状态并添加事件;点击区域外部移除事件。该方法相当于模拟了可聚焦元素,其实并没有这个必要。