资讯动态

用键盘方向键移动页面元素:focus/blur 与 keydown 的实战组合(zh.javascript.info 案例解析)

发布时间:2026/10/9 4:44:36 来源:尧图企业网站定制
文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载本文基于现代 JavaScript 教程zh.javascript.info仓库中「键盘移动老鼠」任务位于 2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/完整拆解一个典型的交互场景点击元素获得焦点后再用键盘方向键移动它。读完本文你将掌握keydown事件的触发条件、tabindextabIndex属性如何让任意元素可聚焦、position:fixed与尺寸属性在自由移动元素时的配合以及一套不依赖修改 HTML/CSS、可复用于任何元素的通用写法。一、任务背景为什么鼠标点击 键盘移动需要专门设计在 5-keyboard-mouse/task.md 中任务要求实现这样一个交互页面上有一个用pre绘制的老鼠图形用户用鼠标点击老鼠使其成为可操作对象随后使用键盘的方向键上、下、左、右移动它每按一次移动一步附加约束除了#mouse元素本身不允许在页面任何其他地方放置事件处理程序另一个约束不允许修改 HTML/CSS方案必须是通用的能适用于任意元素。从交互设计上看这个任务的核心难点并不在于移动本身改style.left/top即可而在于一个容易被忽视的浏览器机制keydown事件只会触发在当前获得焦点的元素上。因此要让键盘事件被老鼠元素接住必须先让老鼠元素获得焦点——这正是 focus/blur 章节2-ui/4-forms-controls/2-focus-blur/article.md知识的实战应用。二、方案设计两个事件、一个关键属性解决方案的总体思路非常简洁来自 solution.md 的原文归纳用mouse.onclick处理鼠标点击把老鼠设置为position:fixed为后续移动做准备用mouse.onkeydown处理键盘方向键修改left/top实现移动唯一的缺陷是keydown仅在聚焦的元素上触发因此必须让老鼠可聚焦——因为不能改 HTML就用mouse.tabIndex属性等价于tabindex特性实现备选也可以用mouse.onfocus代替mouse.onclick作为激活入口。2.1 为什么keydown只认聚焦元素在 7-keyboard-events/article.md 中明确写道按下按键时触发keydown松开时触发keyup。但键盘事件并非全局广播它们会派发到当前拥有焦点的 DOM 元素上。如果一个元素不可聚焦那么键盘事件根本不会落到它身上onkeydown也就永远不会被调用。这就是任务要求中除#mouse外不要在其他地方放事件处理程序的含义我们不能偷懒地把keydown挂到document上做全局监听而必须让老鼠自己可聚焦、能收键。2.2tabindex与tabIndex属性让任意元素可聚焦默认情况下只有button、input、select、a这类可交互元素支持聚焦而div、span、pre等格式化用途的元素默认不可聚焦elem.focus()对它们无效focus/blur事件也不会触发。tabindexHTML 特性可以改变这一点它的取值语义详见 2-ui/4-forms-controls/2-focus-blur/article.md 的「允许在任何元素上聚焦tabindex」小节取值行为tabindex1、2…按数值升序参与 Tab 切换排在无tabindex的元素之前tabindex0与无tabindex的元素同组按文档源顺序参与 Tab 切换通常用于使元素可聚焦但保留默认切换顺序tabindex-1只能通过编程方式聚焦elem.focus()有效Tab 键会跳过它任务要求不修改 HTML所以不能给pre idmouse写tabindex特性。此时可以用 JavaScript 的elem.tabIndex属性——设置属性与设置特性的效果完全一样这是 2-ui/4-forms-controls/2-focus-blur/article.md 中特别强调的我们可以使用elem.tabIndex通过 JavaScript 来添加tabindex。在本题中老鼠元素不是表单的一部分不需要进入 Tab 顺序因此使用tabindex0代码中写作mouse.tabIndex 0最为合适它让pre变成可聚焦元素同时保留默认的 Tab 切换顺序。三、完整实现代码与逐步解读仓库中 solution.view/index.html 给出了可运行的标准答案初始骨架在 source.view/index.htmlscript中只有// ...your code...占位符HTML/CSS 不可改动。完整脚本如下script mouse.tabIndex 0; mouse.onclick function() { this.style.left this.getBoundingClientRect().left px; this.style.top this.getBoundingClientRect().top px; this.style.position fixed; }; mouse.onkeydown function(e) { switch (e.key) { case ArrowLeft: this.style.left parseInt(this.style.left) - this.offsetWidth px; return false; case ArrowUp: this.style.top parseInt(this.style.top) - this.offsetHeight px; return false; case ArrowRight: this.style.left parseInt(this.style.left) this.offsetWidth px; return false; case ArrowDown: this.style.top parseInt(this.style.top) this.offsetHeight px; return false; } }; /script3.1 第一步mouse.tabIndex 0让pre可聚焦这是整个方案成立的前提。没有这一行点击老鼠后它仍不可聚焦onkeydown永远不会触发。设置tabIndex后老鼠元素进入了 Tab 焦点链也就可以通过elem.focus()或直接点击获得焦点。3.2 第二步onclick中完成position:fixed切换点击老鼠时this.style.left this.getBoundingClientRect().left px; this.style.top this.getBoundingClientRect().top px; this.style.position fixed;为什么先读坐标再切fixed因为切换position:fixed后元素的定位基准从文档流变为视口。如果直接设置fixed而不先记录当前坐标元素会立刻跳到视口左上角left/top默认为auto。正确的顺序是用getBoundingClientRect()读出元素当前相对视口的位置left/top转为像素字符串写进style.left/style.top再切换position:fixed此时left/top已有值元素停留在原地视觉上无跳动。这也是 2-ui/3-event-details/4-mouse-drag-and-drop/article.md 拖拽章节中反复使用getBoundingClientRect()读取坐标的同一思路——先取真实位置再基于它计算偏移。3.3 第三步onkeydown中按方向键步进移动case ArrowLeft: this.style.left parseInt(this.style.left) - this.offsetWidth px; return false;移动的关键点方向键的event.key值现代浏览器中方向键的key值为ArrowLeft、ArrowUp、ArrowRight、ArrowDown详见 7-keyboard-events/article.md 中对event.key与event.code的说明方向键这类特殊键两者值基本一致。switch (e.key)按字符判断即可。步长取元素自身尺寸offsetWidth/offsetHeight返回元素布局尺寸含边框不含 margin。每次按方向键就移动恰好一个元素宽度/高度保证按一下走一步、步长与元素大小一致这是让老鼠按格子步进移动的巧妙设计也避免了写死魔法数字。parseInt解析style.left存的是123px这样的带单位字符串parseInt提取出数值参与加减运算再拼回px单位写回。两次相邻按键之间基于同一数值累加坐标不会漂移。return false阻止默认行为在onkeydown处理程序中返回false会阻止该按键的默认行为。对方向键来说默认行为可能是滚动页面返回false确保按方向键时只移动元素、页面不跟着滚动。这也是 7-keyboard-events/article.md 中阻止keydown的默认行为一节的应用。3.4 备选方案用onfocus代替onclick原文档还提示了一个等价入口也可以用mouse.onfocus代替mouse.onclick。因为设置了tabIndex后元素可聚焦无论用户是点击老鼠、还是用Tab键把焦点切到老鼠上都会触发focus事件。把切换position:fixed的逻辑放进onfocus交互入口就从必须点击扩展为点击或 Tab 聚焦皆可。四、代码的通用性为什么它适用于任意元素任务强调不要修改 HTML/CSS这个方法应该是通用的可以用于任何元素。细看实现可以确认通用性来源不依赖任何 ID 之外的硬编码所有操作都通过this引用当前元素步长由offsetWidth/offsetHeight动态计算不关心元素实际尺寸初始位置由getBoundingClientRect()动态读取元素放在哪里就从哪里开始移动聚焦能力通过tabIndex属性注入无需改动 HTML 结构。因此只要把这段逻辑绑定到任意一个设置了tabIndex的元素上例如div、img、button它都能获得点击激活 方向键移动的能力。若要进一步通用化可以把处理逻辑封装成一个函数参数传入元素引用function makeKeyboardMovable(elem) { elem.tabIndex 0; elem.onclick function() { this.style.left this.getBoundingClientRect().left px; this.style.top this.getBoundingClientRect().top px; this.style.position fixed; }; elem.onkeydown function(e) { switch (e.key) { case ArrowLeft: this.style.left parseInt(this.style.left) - this.offsetWidth px; return false; case ArrowUp: this.style.top parseInt(this.style.top) - this.offsetHeight px; return false; case ArrowRight: this.style.left parseInt(this.style.left) this.offsetWidth px; return false; case ArrowDown: this.style.top parseInt(this.style.top) this.offsetHeight px; return false; } }; }以上封装为基于原文档方案的延伸仓库内直接给出了未封装的标准答案版本见 solution.view/index.html。五、进阶注意事项与常见坑结合 focus/blur 与键盘事件两章的知识使用这类方案时需要注意以下几点keydown只认聚焦元素若目标元素不可聚焦未设tabindexonkeydown永远不会触发。若在别的元素上测试失败先检查焦点是否真的在目标元素上可用document.activeElement查看当前聚焦元素。焦点丢失问题如 2-ui/4-forms-controls/2-focus-blur/article.md 中警告的很多情况会导致焦点丢失——用户点击页面其他地方、alert弹出、元素被移出 DOM 等。一旦老鼠失焦方向键就不再生效需要再次点击激活。长按自动重复按住方向键不松keydown会以自动重复的方式持续触发event.repeat为true因此长按会连续移动这是预期行为而非 bug参见 7-keyboard-events/article.md 的「自动重复」小节。event.key与event.code的选择本任务判断方向键用event.key即可因为方向键的key与code一致且不受键盘布局影响。若处理的是字母键热键如CtrlZ则需要斟酌key随语言变化与code绑定物理键位的差异详见 7-keyboard-events/article.md。return false只针对按键默认行为它阻止的是该次keydown的默认动作如页面滚动并不能阻止失去焦点——blur事件发生在失焦之后无法用preventDefault拦截2-ui/4-forms-controls/2-focus-blur/article.md 有专门说明。六、小结「键盘移动老鼠」任务虽然代码不足二十行却串联起了 focus/blur 章节的三大核心知识点focus/blur是理解键盘交互的前提——keydown只派发到聚焦元素tabindex/tabIndex让任意元素可聚焦是通用方案的技术基础focus事件可作为比click更宽的激活入口onfocus替代onclick。同时它也是keydown处理、position:fixed坐标切换、offsetWidth/offsetHeight步进计算的综合练习。这套点击激活 键盘驱动的模式可以平移到游戏网格、菜单导航、无障碍键盘操作等更复杂的交互场景中。相关资源索引任务描述5-keyboard-mouse/task.md标准答案可运行5-keyboard-mouse/solution.view/index.html初始骨架待填空5-keyboard-mouse/source.view/index.html理论章节「聚焦focus/blur」2-ui/4-forms-controls/2-focus-blur/article.md理论章节「键盘keydown 和 keyup」2-ui/3-event-details/7-keyboard-events/article.md赞分享文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载相关推荐JavaScript 键盘驱动鼠标利用 focus/blur 与 keydown 实现方向键移动任意元素JavaScript 键盘驱动鼠标利用 focus/blur 与 keydown 实现方向键移动任意元素 导读 本文基于 Modern JavaScript文档/教程前端Modern JavaScript Tutorial 实战解析用 focus 与 keydown 事件实现“键盘驱动的鼠标”移动组件Modern JavaScript Tutorial 实战解析用 focus 与 keydown 事件实现“键盘驱动的鼠标”移动组件 本篇技术指南围绕 Mod文档/教程前端键盘事件详解在 zh.javascript.info 中掌握 keydown 与 keyup 的完整实战键盘事件详解在 zh.javascript.info 中掌握 keydown 与 keyup 的完整实战 导读 键盘交互是 Web 应用中不可回避的基础能力—文档教程前端上一篇Wireshark ASN.1协议解析开发终极指南从基础到实战下一篇React Native Airbnb Clone10个终极性能优化与内存管理技巧 创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑