5 min read

键盘事件监听不生效的坑,我也踩了

大家好,我是星辰编程理财。最近,我在做一个项目时,遇到了一个问题。事情很简单:如下图,我需要给一个div容器监听keydown事件,然后在页面监听快捷键打开顶部的搜索面板。听起来挺简单对吧?我当时也这么想。

需求图

可是,不管我怎么写代码,keydown事件就是死活不触发!无论是keydownkeyup还是keypress,统统都不工作,我的代码就像没写一样,一片沉寂。这种感觉就像你给朋友发了条消息,却发现他一直在线就是不回你——太扎心了! 😅

解决思路

这种问题首先我还是直接扔给AI解答,但确实出来了一堆废话,效率不高。后来搜索了一遍,大概知道了是没聚焦的问题,于是打算去mdn看下keydown详细文档。最后看详细文档找到了关键的一句话“键盘事件只能由 <inputs>, <textarea> 以及任何具有 contentEditable 或 tabindex="-1"属性的组件触发。”,答案其实在一个属性上——tabindex,给div加个tabindex属性就能搞定这个问题!却让我花了不少时间。 🪄✨

<div
  class="window"
  style={window.styleRecord[window.id]}
  data-window-id={window.id}
  on:keydown={onKeydown}
  tabindex="-1"
>
  {#if $showCmdInfo}
    <CmdInfo windowId={window.id} />
  {/if}
  <Cmd windowId={window.id} />
  <ModalAddCmdAlias windowId={window.id} />
  {#if $search.isOpen}
    <CmdSearchMain windowId={window.id} on:selectedSearchResult={search.close} />
  {/if}
</div>

是不是很简单?只要在div上加上tabindex="-1",就可以愉快地监听键盘事件了!

为什么需要tabindex

重点来了! 我之前一直忽略的一个事实是:默认情况下,只有某些特定的元素(如inputtextareabutton等)才能被“聚焦”(也就是接受用户输入焦点)。而像div这种元素,虽然我们可以往上面写东西,甚至可以监听点击、鼠标悬浮等事件,但它不能接受键盘事件,因为它不能聚焦。

tabindex就是为了解决这个问题的。它可以让任何元素变得可以被聚焦,从而接受键盘事件。

tabindex的使用小窍门

你可能会想:“嘿,这个tabindex到底是啥?” 简单来说,tabindex是HTML元素的一个属性,它用来控制元素是否能通过键盘获取焦点,以及元素之间切换焦点的顺序。

tabindex的取值解释:

  1. tabindex="0":元素可以通过键盘导航获取焦点,并且焦点顺序会按照文档的结构顺序进行。如果你给一个div加上tabindex="0",它就能像input一样接受键盘事件。

  2. tabindex="-1":元素不能通过键盘导航获得焦点,但仍然可以通过JavaScript调用focus()方法来聚焦它。这种用法通常在一些复杂的自定义组件里很有用,比如我们想要通过编程来控制元素的焦点。

  3. tabindex大于0:元素可以通过键盘导航获得焦点,并且优先级比tabindex="0"更高,数值越小优先级越高。如果你给多个元素设置了不同的tabindex值,按下Tab键时它们会按照这个数值的顺序依次获得焦点。

问题解析:原理是什么?

回想一下刚开始为什么keydown事件监听不到——其实问题的根源就在于焦点。浏览器里,键盘事件默认只触发在能够聚焦的元素上,而div这样的元素默认是不能聚焦的。tabindex就是用来改变这一行为的,它让非表单元素也能接受键盘输入。

所以,我们给div加上了tabindex,然后通过dom.focus()让它真正获得焦点,这样键盘事件就可以正常触发了。

代码示例

来个完整的例子吧!当用户点击div时,聚焦到这个元素,然后监听键盘输入,并且将用户按下的键展示在页面上。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Keyboard Event Example</title>
  <style>
    #editor {
      width: 300px;
      height: 100px;
      border: 2px solid #333;
      display: flex;
      justify-content: center;
      align-items: center;
      font-size: 18px;
      cursor: pointer;
    }
  </style>
</head>
<body>

<div id="editor" tabindex="0">
  键盘事件
</div>

<script>
  const dom = document.getElementById('editor');

  // 点击时聚焦到div元素
  dom.addEventListener('click', () => dom.focus());

  // 监听keydown事件
  dom.addEventListener('keydown', (e) => {
    dom.textContent = `keydown: ${e.key}`;
  });
</script>

</body>
</html>

这个例子很简单:当你点击这个div后,它会获得焦点,之后按下任何键盘键,页面上会显示你按下了什么键。🎉

小结

这是一个非常基础的问题,但在前端开发中大概率容易疏忽掉。前端知识点很多,不能一一记住,还得具体问题具体分析,才能游刃有余地应对各种需求。 👨‍💻👩‍💻