Vue中实现回车键切换焦点的方法


Posted in Javascript onFebruary 19, 2020

几乎在所有浏览器中,都具有 Tab 键切换焦点的功能。

但是任性的用户强烈要求一定要有 Enter 键切换焦点的功能。

为了交付上线拿到钱,我们只好再一次毫无原则性的接受了客户的需求。

在上一代人中,大多都有这种操作习惯。习惯把保存成为编辑,习惯用回车替换 Tab。这是受到微软 excel 荼毒的结果。

起初我以为这个功能很简单,无非就是把 Enter 键的功能转接到 Tab 键上面,分分钟就可以解决掉的问题。

可困难马上就出现了,我发现这条路是走不通的。

我们经常可以主动触发某个事件,比如 el.click() 就可以调用点击事件,或者使用 dispatchEvent 。但是键盘和鼠标事件却不行。

我查阅了很多资料,也做了很多尝试。最后总结出来一个结论,在浏览器中,JavaScript 无法操作用户的键盘或者鼠标,这是出于安全策略的考虑。仔细想一下,如果可以用一段 JavaScript 脚本控制用户键盘和鼠标的话,那么用户只需要打开一个黑客网站,黑客就可以瞬间得到他想得到的一切。

所以,如果要通过除 Tab 键以外的其他方式来触发焦点切换, focus 几乎是唯一的选择。

在原生页面中实现回车键切换焦点

项目是基于 vue 和 element-ui 做的,为了把实现思路先讲清楚,暂时把这些抛开,从原生的页面中寻找答案。

以下是一个原生的 html 页面。

<!DOCTYPE html>
<html>
 <head>
 <meta charset="utf-8" />
 <meta name="viewport" content="width=device-width" />
 <title>Demo</title>
 </head>
 <body>
 <form>
 <input placeholder="姓名" />
 <input placeholder="性别" />
 <input placeholder="年龄" />
 </form>
 </body>
</html>

接下来要实现通过回车键切换焦点,我把思路梳理如下:

  1. 监听回车键按下事件。
  2. 获取当前聚焦元素。
  3. 获取下一个要被聚焦的元素。
  4. 切换焦点。

思路有了,实现起来也非常简单。

1.监听回车键按下事件

在文档中添加 script 标签,写入如下代码。

function enterCallback(e) {
 if (e.keyCode === 13) {
 // 按下回车后的逻辑
 }
}
window.addEventListener("keydown", enterCallback);

要注意, enterCallback 单独拿出来,用于注销监听事件。

监听按键事件最常用的方法就是使用事件委托,将事件绑定到 window 对象上。相比较给每一个元素都绑定一个事件的方式,这样做的最大好处就是节省内存空间,性能更好。

判断按下哪个键的方式有很多,比如判断 e.keye.code 或者 e.keyCode 等方式。但绝大多数的情况下都建议使用 e.keyCode 。下面是一张来自网络的 keyCode 表。

Vue中实现回车键切换焦点的方法

2.获取当前聚焦元素

很容易就可以做到这一步。

常见的有两种方式。第一种是 e.target ,第二种是 document.activeElement 。这种情况下,个人更推荐使用第二种。

function enterCallback(e) {
 if (e.keyCode === 13) {
 let activeEl = document.activeElement;
 }
}

3.获取下一个要被聚焦的元素

这一步也比较容易。使用 el.nextElementSibling API 即可获取。

function enterCallback(e) {
 if (e.keyCode === 13) {
 let activeEl = document.activeElement;
 let nextEl = activeEl.nextElementSibling;
 }
}

4.切换焦点

切换焦点调用 focus 即可实现。

function enterCallback(e) {
 if (e.keyCode === 13) {
 let activeEl = document.activeElement;
 let nextEl = activeEl.nextElementSibling;
 nextEl && nextEl.focus();
 }
}

至此一个最简单的 Demo 已经实现了,接下来看看项目中实际的情况。

在 element-ui 项目中实现回车键切换焦点

因为是使用组件开发,加上样式等因素,dom 节点并不像上面写的原生 Demo 那么简单,实际情况是多层嵌套的。下面是实际生成的代码结构。

<div
 class="el-form-item el-form-item--small"
 style="margin-bottom: 0vh; width: 25%; display: inline-block;"
>
 <label for="pactcode" class="el-form-item__label" style="width: 130px;"
 >协议号</label
 >
 <div class="el-form-item__content" style="margin-left: 130px;">
 <div class="el-input el-input--small">
 <!---->
 <input
 type="text"
 autocomplete="off"
 id="el-input"
 placeholder="未填写协议号"
 class="el-input__inner"
 />
 <!---->
 </div>
 </div>
</div>

可以看到,如果每一个输入框都是这种类型的嵌套结构,上面的方法是无法直接解决的。因为 nextElementSibling API 只能找到下一个兄弟元素,而在这里 input 明显找不到下一个兄弟元素。

思路是,通过回溯的手段朝外层寻找,直到找到一个类名包含 el-form-itemel-form-item--small 的祖级元素,然后再从这个祖级元素的下一个兄弟元素中寻找类名包含 el-input__inner 的 input 元素。

所以要再写两个函数,分别是寻找组件元素的 findFormItem 和寻找 input 元素的 findInput

findFormItem:

function findFormItem(el) {
 const parent = el.parentElement;
 if (!parent) return document.body;
 if (
 parent.className.includes("el-form-item") &&
 parent.className.includes("el-form-item--small")
 ) {
 return parent;
 }
 return findFormItem(parent);
}

findInput:

function findInput(container) {
 let nextEl = container.nextElementSibling;
 if (!nextEl) return;
 let input = nextEl.querySelector("input");
 while (input.id === "el-select") {
 nextEl = nextEl.nextElementSibling;
 if (!nextEl) return;
 input = nextEl.querySelector("input");
 }
 if (input.className.includes("el-input__inner")) return input;
}

有了这两个函数以后,实现回车切换焦点就非常简单了。只需要执行两行代码。

const container = findFormItem(document.activeElement);
findInput(container) && findInput(container).focus();

完整的代码大概是这样的。

methods 中声明三个方法。

methods: {
 addEnterListener() {
 if (window.__completeEnterBind__) return;
 window.addEventListener("keydown", this.enterCallback);
 window.__completeEnterBind__ = true;
 },
 removeEnterListener() {
 window.removeEventListener("keydown", this.enterCallback);
 window.__completeEnterBind__ = false;
 },
 enterCallback(e) {
 function findFormItem(el) {
 const parent = el.parentElement;
 if (!parent) return document.body;
 if (
  parent.className.includes("el-form-item") &&
  parent.className.includes("el-form-item--small")
 ) {
  return parent;
 }
 return findFormItem(parent);
 }
 function findInput(container) {
 let nextEl = container.nextElementSibling;
 if (!nextEl) return;
 let input = nextEl.querySelector("input");
 while (input.id === "el-select") {
  nextEl = nextEl.nextElementSibling;
  if (!nextEl) return;
  input = nextEl.querySelector("input");
 }
 if (input.className.includes("el-input__inner")) return input;
 }
 if (e.keyCode === 13) {
 const container = findFormItem(document.activeElement);
 findInput(container) && findInput(container).focus();
 }
 }
}

然后在 mounted 中添加回车监听和在 destroy 中移除回车键听。

mounted() {
 this.addEnterListener();
},
destroy() {
 this.removeEnterListener();
},

需要注意的是,项目是多标签页的形式,表单组件可能会被渲染多次,所以通过在 window 对象上添加一个 __completeEnterBind__ 字段来确保回车换行事件正确绑定。

总结

以上所述是小编给大家介绍的Vue中实现回车键切换焦点的方法,希望对大家有所帮助,也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
JavaScript对象、属性、事件手册集合方便查询
Jul 04 Javascript
在iframe里的页面编写js,实现在父窗口上创建动画效果展开和收缩的div(不变动iframe父窗口代码)
Dec 20 Javascript
js鼠标点击事件在各个浏览器中的写法及Event对象属性介绍
Jan 24 Javascript
JavaScript表单通过正则表达式验证电话号码
Mar 14 Javascript
js实现延迟加载的方法
Jun 24 Javascript
探讨跨域请求资源的几种方式(总结)
Dec 02 Javascript
不得不看之JavaScript构造函数及new运算符
Aug 21 Javascript
jQueryMobile之窗体长内容的缺陷与解决方法实例分析
Sep 20 jQuery
前端必备插件之纯原生JS的瀑布流插件Macy.js
Nov 22 Javascript
CKEditor 4.4.1 添加代码高亮显示插件功能教程【使用官方推荐Code Snippet插件】
Jun 14 Javascript
三步实现ionic3点击退出app程序
Sep 17 Javascript
详解Howler.js Web音频播放终极解决方案
Aug 23 Javascript
整理 node-sass 安装失败的原因及解决办法(小结)
Feb 19 #Javascript
Vue常用的全选/反选的示例代码
Feb 19 #Javascript
详解node和ES6的模块导出与导入
Feb 19 #Javascript
JS实现分页导航效果
Feb 19 #Javascript
vue随机验证码组件的封装实现
Feb 19 #Javascript
JavaScript实现PC端四格密码输入框功能
Feb 19 #Javascript
vue实现带过渡效果的下拉菜单功能
Feb 19 #Javascript
You might like
Oracle Faq(Oracle的版本)
2006/10/09 PHP
Apache中php.ini的设置方法
2013/02/28 PHP
Thinkphp5.0自动生成模块及目录的方法详解
2017/04/17 PHP
php学习笔记之mb_strstr的基本使用
2018/02/03 PHP
PHP的mysqli_thread_id()函数讲解
2019/01/24 PHP
javascript编程起步(第一课)
2007/01/10 Javascript
60个很实用的jQuery代码开发技巧收集
2014/12/15 Javascript
js使用setTimeout实现定时炸弹的方法
2015/04/10 Javascript
JS实现弹性菜单效果代码
2015/09/07 Javascript
Bootstrap框架下下拉框select搜索功能
2020/03/26 Javascript
使用Node.js实现RESTful API的示例
2017/08/01 Javascript
简单介绍react redux的中间件的使用
2018/04/06 Javascript
JS获取浏览器地址栏的多个参数值的任意值实例代码
2018/07/24 Javascript
ztree加载完成后显示勾选节点的实现代码
2018/10/22 Javascript
微信小程序下拉框组件使用方法详解
2018/12/28 Javascript
详解vue-router导航守卫
2019/01/19 Javascript
微信小程序 高德地图路线规划实现过程详解
2019/08/05 Javascript
JavaScript写个贪吃蛇小游戏(超详细)
2020/03/17 Javascript
JavaScript 声明私有变量的两种方式
2021/02/05 Javascript
用python实现简单EXCEL数据统计的实例
2017/01/24 Python
Django rest framework工具包简单用法示例
2018/07/20 Python
python之yield和Generator深入解析
2019/09/18 Python
HTML5 canvas基本绘图之绘制阴影效果
2016/06/27 HTML / CSS
Clarins娇韵诗英国官网:来自法国的天然护肤品牌
2017/04/18 全球购物
NBA欧洲商店(英国):NBA Europe Store UK
2018/07/27 全球购物
世界上最好的野生海鲜和有机食品:Vital Choice
2020/01/16 全球购物
Michael Kors澳大利亚官网:世界知名的奢侈饰品和成衣设计师
2020/02/13 全球购物
*p++ 自增p 还是p所指向的变量
2016/07/16 面试题
物流仓管员岗位职责
2013/12/04 职场文书
大学生实习感言
2014/01/16 职场文书
酒店端午节促销方案
2014/02/18 职场文书
医院院务公开实施方案
2014/05/03 职场文书
三八妇女节趣味活动方案
2014/08/23 职场文书
大学生学生会工作总结2015
2015/05/26 职场文书
运动会闭幕式致辞
2015/07/29 职场文书
phpQuery解析HTML乱码问题(补充官网未列出的乱码解决方案)
2021/04/01 PHP