Vue.js通过ref获取input元素调用focus报错解决方法
问题原因
- 核心报错原因:你通过
this.$refs[ref]拿到的是原生<input>DOM元素,不是自定义Vue组件实例,只有Vue组件实例上才有$el属性,原生DOM元素本身就自带focus()方法,你多加了一层$el调用,自然会拿到undefined。 - 之前试
$nextTick没生效的原因也很简单:这不是DOM渲染时机的问题,是属性访问路径写错了,等多久都不会存在$el属性。 - 现有代码还有几个隐藏问题:
- 三个输入框都加了
autofocus属性,页面加载时只有最后一个输入框会自动获得焦点,前两个的autofocus完全无效 - 内联写的原生
oninput事件里的this指向DOM元素本身,虽然当前跳转逻辑碰巧能跑,但不符合Vue的写法规范,后续加逻辑很容易出上下文错误 - 手动重复写3个输入框的逻辑冗余度太高,后续要改6位/4位验证码要重复改很多代码
- 现在删除跳转的逻辑没有判断当前输入框是否为空,只要按delete就跳,会导致输入框有内容时,用户按删除键删完内容直接跳走,不符合正常交互预期
- 三个输入框都加了
解决方法
最小改动修复(不用调整现有组件结构)
直接把handleDeleteClick里的$el去掉即可,同时补充空值判断避免交互异常:
handleDeleteClick(prevRef, currentValueKey) { // 只有当前输入框为空时,按删除才跳转到前一个输入框 if (this[currentValueKey] === '') { this.$refs[prevRef].focus() } }
对应模板里的事件绑定同步调整传参:
- n2输入框的删除事件改成
@keyup.delete="handleDeleteClick('n1', 'i2')" - n3输入框的删除事件改成
@keyup.delete="handleDeleteClick('n2', 'i3')"
优化重构方案(易维护,支持任意长度验证码)
建议把重复的输入框用v-for循环渲染,去掉冗余的重复代码,同时把内联的input事件抽成Vue方法,去掉多余的无效autofocus,修正后完整代码如下:
<template> <div :class="onwhite ? 'on-white' : ''"> <input v-for="(item, index) in codeLength" :key="index" :ref="`n${index+1}`" v-model="codeList[index]" class="input-two-fa center" type="number" :disabled="disabled" min="0" max="9" autocomplete="off" :autofocus="index === 0" @input="handleInput(index)" @keyup.delete="handleDelete(index)" > </div> </template> <script> import { validate2fa } from "~/helpers/frontValidator"; export default { name: "InputTwoFa", props: { disabled: { type: Boolean, default: false }, onwhite: { type: Boolean, default: false }, // 支持自定义验证码位数,默认3位做演示 codeLength: { type: Number, default: 3 } }, data() { return { codeList: Array(this.codeLength).fill('') } }, watch: { codeList: { deep: true, handler(val) { this.$emit('returnTwoFa', val) } } }, methods: { handleInput(index) { // 过滤非法输入 this.codeList[index] = validate2fa(this.codeList[index]) // 输入有效内容后自动跳转到下一个输入框,最后一个输入框不跳转 if (this.codeList[index] !== '' && index < this.codeLength - 1) { // v-for绑定的ref返回值是数组,取[0]拿到对应DOM this.$refs[`n${index+2}`][0].focus() } }, handleDelete(index) { // 当前输入框为空且不是第一个输入框时,跳转到前一个输入框 if (this.codeList[index] === '' && index > 0) { this.$refs[`n${index}`][0].focus() } } } } </script>
注意:用v-for循环生成ref时,
this.$refs.xxx拿到的是DOM元素数组,所以要取[0]才能拿到对应的input元素,这是循环渲染ref时的常见坑。
内容的提问来源于stack exchange,提问作者dokichan
相关产品推荐
相关产品推荐

