我创建了一个具有以下行为的4引脚字段:当一个字段被填充时,焦点转移到下一个字段(光标切换到下一个输入字段)。在删除文本值时,该输入字段中的文本将被删除,光标移动到其前面的pin字段。
问题是,有时当带有文本的字段被突出显示时(例如在删除之后),并且键入另一个键以覆盖前一个值(在错误的情况下),input
事件不会触发,使光标仍然在该字段中(光标不移动到下一个引脚字段)。这显然不会带来很好的用户体验,我希望在高亮显示的文本被覆盖时始终切换引脚字段。
代码的相关部分如下所示。
data() {
return {
focusIndex: 1,
inputOne: '',
inputTwo: '',
inputThree: '',
inputFour: '',
numberOfPinFields: 4,
};
},
mounted() {
// this highlights the first pin field
this.$refs[this.focusIndex].focus();
},
methods: {
handlePinFieldDeletion() {
if (this.focusIndex > 1) {
this.focusIndex -= 1;
}
},
handlePinFieldInput(value, maxNumberOfFields) {
this.$nextTick(() => {
// after the focus index from the input field watch below is updated, increase the focusIndex value
if (value.length === 1 && this.focusIndex < maxNumberOfFields) {
this.focusIndex += 1;
}
});
},
ensurePinFieldHasOneInput(value) {
return value.slice(0, 1);
},
highlightOnFocus(e, focusIndex) {
// highlight the text
e.target.select();
// set the new focus index
this.focusIndex = focusIndex;
},
}
watch: {
focusIndex(newValue) {
this.$refs[newValue].focus();
},
inputOne(newValue) {
// set focus index of first otp input field to 1 when it changes.
// This will help with situations where the user doesn't use the input fields in numerical order
this.focusIndex = 1;
this.inputOne = this.ensurePinFieldHasOneInput(newValue);
},
inputTwo(newValue) {
// set focus index of first otp input field to 2 when it changes.
// This will help with situations where the user doesn't use the input fields in numerical order
this.focusIndex = 2;
this.inputTwo = this.ensurePinFieldHasOneInput(newValue);
},
inputThree(newValue) {
// set focus index of first otp input field to 3 when it changes.
// This will help with situations where the user doesn't use the input fields in numerical order
this.focusIndex = 3;
this.inputThree = this.ensurePinFieldHasOneInput(newValue);
},
inputFour(newValue) {
// set focus index of first otp input field to 4 when it changes.
// This will help with situations where the user doesn't use the input fields in numerical order
this.focusIndex = 4;
this.inputFour = this.ensurePinFieldHasOneInput(newValue);
},
},
<form>
...
<q-input
type="password"
input-class="text-center"
maxlength="1"
@keyup.delete="handlePinFieldDeletion"
@input="handlePinFieldInput($event, numberOfPinFields)"
v-number-only
@focus="highlightOnFocus($event, 1)"
borderless
ref="1"
v-model="inputOne"
/>
<q-input
type="password"
input-class="text-center"
maxlength="1"
v-number-only
@focus="highlightOnFocus($event, 2)"
@keyup.delete="handlePinFieldDeletion"
@input="handlePinFieldInput($event, numberOfPinFields)"
borderless
ref="2"
v-model="inputTwo"
/>
<q-input
type="password"
input-class="text-center"
maxlength="1"
v-number-only
@focus="highlightOnFocus($event, 3)"
@keyup.delete="handlePinFieldDeletion"
@input="handlePinFieldInput($event, numberOfPinFields)"
borderless
ref="3"
v-model="inputThree"
/>
<q-input
type="password"
input-class="text-center"
v-number-only
@focus="highlightOnFocus($event, 4)"
maxlength="1"
@keyup.delete="handlePinFieldDeletion"
@input="handlePinFieldInput($event, numberOfPinFields)"
borderless
ref="4"
v-model="inputFour"
/>
...
</form>
我做了一些测试,我看到了错误在手表当重写相同的值,即字段1突出显示了5,并再次更新了5。
代码:
watch: {
focusIndex(newValue) {
this.$refs[newValue].focus();
},
inputOne(newValue) {
this.$q.notify({
message: 'inputOne changed',
caption: 'moments ago',
color: 'secondary'
})
当输入字段中包含最大数量的值时,maxLength
属性不允许触发input
事件。
然而,什么导致引脚字段有时工作后,突出显示的值被覆盖,而不是工作其他时间,是当用户输入相同的值是什么之前有(即覆盖的值是相同的前一个值),input
事件将不会被触发,而一旦不同的值被用来覆盖前一个值,input
事件被触发。
我通过反复试验发现了这一点。我只能假设这是默认的浏览器行为,因为我无法在其他地方确认。
为了解决这个问题,我删除了maxLength
属性,并重构了代码。,它是有效的。重构后的代码如下所示。
<script>
const generatePinInputs = (numberOfPinFields) => {
const objectOfPinFields = {};
// create an array of values of the number of pin fields
Array(numberOfPinFields).fill('').forEach((field, index) => {
objectOfPinFields[index + 1] = field;
});
return objectOfPinFields;
};
export default {
name: 'app-pin',
props: {
numberOfPinFields: {
type: Number,
default: 4,
},
},
mounted() {
// switched from quasar's q-input to input because of error thrown about v-model mutating prop value
this.$nextTick(() => {
// After all asynchronous actions (including dialog popup showing pin fields), focus on the first input
this.$refs[1][0].focus();
});
},
data() {
return {
pinInputs: generatePinInputs(this.numberOfPinFields),
};
},
computed: {
pinFilled() {
return Object.values(this.pinInputs).join('');
},
},
watch: {
pinFilled(newValue) {
// if all 4 pin fields have been completed, emit completed
if (newValue.length === this.numberOfPinFields) {
this.$emit('completed', newValue);
}
},
},
methods: {
handleInput(e, index) {
const { value } = e.target;
// if there is no value, do nothing - the deletion of a value also calls the input value
// this prevents an error when a value is deleted
if (!value) {
return;
}
// replace the data property with the latest value that is typed
this.pinInputs[index] = value.slice(-1);
// sometimes this is required by vue to override default browser behaviour
this.$refs[index][0].value = value.slice(-1);
if (index !== this.numberOfPinFields) {
this.$refs[index + 1][0].focus();
}
},
handleDelete(index) {
if (index !== 1) {
this.$refs[index - 1][0].focus();
}
},
},
};
</script>
<template>
<transition appear enter-active-class="animated slideInRight">
<form class="appPin">
<input
v-for="(index) in numberOfPinFields"
:key="index"
input-class="text-center"
@input="handleInput($event, index)"
@keyup.delete="handleDelete(index)"
v-number-only
type="password"
inputmode="numeric"
:ref="index"
v-model="pinInputs[index]"
/>
</form>
</transition>
</template>