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

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

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

解决思路

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

html 复制代码
<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时,聚焦到这个元素,然后监听键盘输入,并且将用户按下的键展示在页面上。

html 复制代码
<!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后,它会获得焦点,之后按下任何键盘键,页面上会显示你按下了什么键。🎉

小结

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

相关推荐
flash俊杰12 小时前
Electron 桌面应用的进程模型:为什么 fork Next.js standalone
前端
沙蒿同学12 小时前
我把架构约定编译成了会变红的测试:Wails v2 + Go + Vue3 桌面脚手架实战
前端·后端·github
cpolar技术支持12 小时前
本地 Playwright 测试报告怎么远程复盘?Trace Viewer 跑起来后,用 cpolar 分享失败现场
前端·自动化测试·测试工具·cpolar·playwright
wordbaby12 小时前
企业级后台管理系统路由设计与最佳实践指南
前端
胡志辉的博客12 小时前
【完全开源】IP 纯净度检测 可一键部署到自己的CF
前端·javascript·chrome·ip·chromium
Hilaku13 小时前
作为面试官,我最怕遇到什么样的候选人?
前端·javascript·程序员
TiDi13 小时前
Pinia优化重复请求
前端
web3d52013 小时前
01-用 Leafletjs 10 分钟搭一张水利一张图(Vue3 + Vite 实战)
前端·javascript
晚安日记wanna13 小时前
Vue2 的 defineProperty 差在哪四层追问筛掉九成候选人
前端·vue.js·面试
TiDi13 小时前
吸顶导航交互实现
前端