修改输入框值时绑定的v-model未同步问题求助
问题:屏幕键盘修改输入框值后v-model未同步更新
我正在实现一个屏幕数字键盘,用于给数十个输入框输入数字。目前通过focus事件记录当前激活的输入框,点击数字按键时把数字追加到输入框值末尾,但这样修改输入框值后,绑定的v-model数据并没有同步变化,用普通键盘输入却一切正常。
示例代码:
<template> <p>xSize</p> <input v-model="xSize"> <p>ySize</p> <input v-model="ySize"> <div class="keyboard"> <button v-for="i in [1,2,3,4,5,6,7,8,9,0]" class="key" :key="i" @click="addChar(i)">{{i}}</button> </div> <p>xSize = {{xSize}}</p> <p>ySize = {{ySize}}</p> <div class="test-box" v-bind:style="{width: this.xSize + 'px', height: this.ySize + 'px'}"></div> </template> <script> export default { data: function () { return { xSize: 50, ySize: 50, activeElement: null } }, props: {}, created() { document.addEventListener('focusin', this.focusChanged) }, beforeUnmount() { document.removeEventListener('focusin', this.focusChanged) }, methods: { focusChanged: function (event) { const el = event.target if (el.type === "text") { if (this.activeElement !== el && this.activeElement !== null) { this.activeElement.style.background = "white"; } this.activeElement = el this.activeElement.style.background = "red"; } }, addChar: function (char) { if (this.activeElement !== null) { this.activeElement.value += char } } } } </script> <style scoped> .keyboard { display: flex; } .key { margin: 3px; width: 30px; height: 30px; border: 2px black ridge; } .test-box { border: 2px black ridge; background: red; margin: 15px; } </style>
问题原因
直接修改DOM元素的value属性,不会触发Vue的响应式更新机制。v-model的双向绑定依赖于input事件,手动修改DOM值不会触发这个事件,所以Vue无法感知到值的变化,也就不会更新对应的data属性。
解决方法
方法一:手动触发input事件(改动小)
修改addChar方法,在修改DOM值后手动触发input事件,让Vue捕获到变化:
addChar: function (char) { if (this.activeElement !== null) { this.activeElement.value += char; // 触发input事件,通知Vue更新v-model绑定的数据 this.activeElement.dispatchEvent(new Event('input')); } }
方法二:直接操作data属性(更符合Vue思想)
放弃直接操作DOM,改为记录当前激活输入框对应的data字段名,点击按键时直接修改data值,利用Vue的响应式自动同步到输入框。
修改后的完整代码:
<template> <p>xSize</p> <!-- 用data-field绑定对应的data字段 --> <input v-model="xSize" data-field="xSize"> <p>ySize</p> <input v-model="ySize" data-field="ySize"> <div class="keyboard"> <button v-for="i in [1,2,3,4,5,6,7,8,9,0]" class="key" :key="i" @click="addChar(i)">{{i}}</button> </div> <p>xSize = {{xSize}}</p> <p>ySize = {{ySize}}</p> <div class="test-box" :style="{width: xSize + 'px', height: ySize + 'px'}"></div> </template> <script> export default { data() { return { xSize: 50, ySize: 50, activeField: '' // 记录当前激活的data字段名 } }, created() { document.addEventListener('focusin', this.focusChanged) }, beforeUnmount() { document.removeEventListener('focusin', this.focusChanged) }, methods: { focusChanged(event) { const el = event.target if (el.type === "text" && el.dataset.field) { // 移除之前激活输入框的样式 if (this.activeField) { const prevEl = document.querySelector(`[data-field="${this.activeField}"]`); if (prevEl) prevEl.style.background = "white"; } // 记录当前激活的字段名 this.activeField = el.dataset.field; el.style.background = "red"; } }, addChar(char) { if (this.activeField) { // 直接修改data中的对应字段,Vue自动同步到输入框 this[this.activeField] = `${this[this.activeField]}${char}`; } } } } </script> <style scoped> .keyboard { display: flex; } .key { margin: 3px; width: 30px; height: 30px; border: 2px black ridge; } .test-box { border: 2px black ridge; background: red; margin: 15px; } </style>
推荐说明
方法二更适合大量输入框的场景,完全遵循Vue数据驱动的思想,不需要直接操作DOM,只需要给每个输入框添加data-field属性绑定对应的data字段即可,代码更易维护。
内容的提问来源于stack exchange,提问作者Ruslan54
相关产品推荐
相关产品推荐

