淺談vue限制文本框輸入數(shù)字的正確姿勢
最近遇到一個需求,需要限制文本框輸入數(shù)字,而number類型的輸入框有箭頭,個人不是很喜歡,因此想要尋求其它途徑實現(xiàn)。本想通過網(wǎng)上找個現(xiàn)成的插件,然而百度,谷歌一番都沒有找到滿意的答案,至于隨手一搜出來的方案或多或少都有點缺陷。因此自己動手,豐衣足食。
事件選型
首先我們很容易想到通過事件來達到目的,大致可以通過以下幾個事件來實現(xiàn):
- keypress/keydown
思路:按鍵按下的時候觸發(fā),通過判斷按下的是否是數(shù)字返回true/false來限制用戶的輸入。
缺點:無法限制用戶復(fù)制粘貼的數(shù)據(jù)。
- keyup
思路:同keypress事件,區(qū)別在于按鍵摁下彈起的時候觸發(fā)。
缺點:從界面上看有個回退(顯示用戶輸入的 不合法字符之后刪除回退)的現(xiàn)象,不是很自然。
- input
思路:用戶輸入的時候觸發(fā)
優(yōu)點:可以在用戶復(fù)制粘貼的時候獲取到完整的值,同時不會有界面回退的現(xiàn)象。
初步實現(xiàn)
綜合各種方案的優(yōu)缺點,選定input事件來實現(xiàn),首先我們來實現(xiàn)最簡單的限制用戶只能輸入正整數(shù)。代碼如下:
<el-input v-model="model" oninput="value=value.replace(/[^\d]/g, '')" />
但是,此方法有的時候沒有很好地達到預(yù)期效果,這邊我放到最后再來講。
封裝成指令
通過自定義指令來增加相應(yīng)DOM的特性,使其支持文本輸入限制。
function onInput(el, ele, binding, vnode) {
function handle() {
// 只保留數(shù)字
ele.value = ele.value.replace(/[^\d]/g, '')
}
return handle
}
const numberInput= {
bind(el, binding, vnode) {
const ele = el.tagName === 'INPUT' ? el : el.querySelector('input')
ele.addEventListener('input', onInput(el, ele, binding, vnode), false)
},
}
Vue.directive('number-input', numberInput)
使用方法:
<el-input v-model="model" v-number-input />
支持浮點數(shù)
我們知道v-model指令有三種修飾符lazy, number, trim實現(xiàn)了不同的功能,這邊呢,也想通過v-number-input.float的方式達到支持浮點數(shù)的目的 。
修改handle回調(diào),增加浮點數(shù)的支持
function handle() {
+ let val = ele.value
+ // modifiers為修飾符對象,傳入了float,則其float屬性為true
+ if (binding.modifiers.float) {
+ // 清除"數(shù)字"和"."以外的字符
+ val = val.replace(/[^\d.]/g, '')
+ // 只保留第一個, 清除多余的
+ val = val.replace(/\.{2,}/g, '.')
+ // 第一個字符如果是.號,則補充前綴0
+ val = val.replace(/^\./g, '0.')
+ } else {
val = ele.value.replace(/[^\d]/g, '')
}
ele.value = val
}
使用方法:
<el-input v-model="model" v-number-input.float />
支持小數(shù)保留位最大位數(shù)限制
查閱官方文檔可知,指令支持參數(shù)的傳遞,這邊我們期望通過參數(shù)傳遞小數(shù)點保留位數(shù)的配置
function handle() {
let val = ele.value
// modifiers為修飾符對象,傳入了float,則其float屬性為true
if (binding.modifiers.float) {
// 清除"數(shù)字"和"."以外的字符
val = val.replace(/[^\d.]/g, '')
// 只保留第一個, 清除多余的
val = val.replace(/\.{2,}/g, '.')
// 第一個字符如果是.號,則補充前綴0
val = val.replace(/^\./g, '0.')
+ if (typeof binding.value !== 'undefined') {
+ // 期望保留的最大小數(shù)位數(shù)
+ let pointKeep = 0
+ if (typeof binding.value === 'string'
+ || typeof binding.value === 'number') {
+ pointKeep = parseInt(binding.value)
+ }
+ if (!isNaN(pointKeep)) {
+ if (!Number.isInteger(pointKeep)
+ || pointKeep < 0) {
+ pointKeep = 0
+ }
+ const str='^(\\d+)\\.(\\d\{' + pointKeep + '}).*$'
+ const reg=new RegExp(str)
+ if (pointKeep === 0) {
+ // 不需要小數(shù)點
+ val = val.replace(reg, '$1')
+ } else {
+ // 通過正則保留小數(shù)點后指定的位數(shù)
+ val = val.replace(reg, '$1.$2')
+ }
+ }
} else {
val = ele.value.replace(/[^\d]/g, '')
}
ele.value = val
}
使用方法:
// 最多支持保留2位小數(shù) <el-input v-model="model" v-number-input.float="2" />
支持負數(shù)
對正則稍加修改,只保留開頭的負號,使其支持負數(shù)
function handle() {
let val = ele.value
// modifiers為修飾符對象,傳入了float,則其float屬性為true
if (binding.modifiers.float) {
// 清除"數(shù)字"和"."以及"-"以外的字符
+ val = val.replace(/[^\-\d.]/g, '')
// 只保留第一個'-'號
+ val = val.replace(/\.{2,}/g, '.').replace(/\-{2,}/g, '-')
// 將 '-.' 替換成 '-0.'
+ val = val.replace(/^\./g, '0.').replace(/^\-\./, '-0.')
if (typeof binding.value !== 'undefined') {
// 期望保留的最大小數(shù)位數(shù)
let pointKeep = 0
if (typeof binding.value === 'string'
|| typeof binding.value === 'number') {
pointKeep = parseInt(binding.value)
}
if (!isNaN(pointKeep)) {
if (!Number.isInteger(pointKeep)
|| pointKeep < 0) {
pointKeep = 0
}
// 增加'-'號的支持
+ const str='^(\\-)*(\\d+)\\.(\\d\{' + pointKeep + '}).*$'
const reg=new RegExp(str)
if (pointKeep === 0) {
// 不需要小數(shù)點
+ val = val.replace(reg, '$1$2')
} else {
// 通過正則保留小數(shù)點后指定的位數(shù)
+ val = val.replace(reg, '$1$2.$3')
}
}
} else {
val = ele.value.replace(/[^\d]/g, '')
}
ele.value = val
}
支持輸入的最大值和最小值的限制
指令的參數(shù)支持對象的傳遞,因此這邊我們通過對象的字段配置最大數(shù)和最小值的配置。定義如下:
{
min: {type: number},
max: {type: number},
// 最大小數(shù)位,僅在float下有效
decimal: {type: number}
}
function handle() {
let val = ele.value
// modifiers為修飾符對象,傳入了float,則其float屬性為true
if (binding.modifiers.float) {
// 清除"數(shù)字"和"."以及"-"以外的字符
val = val.replace(/[^\-\d.]/g, '')
// 只保留第一個'-'號
val = val.replace(/\.{2,}/g, '.').replace(/\-{2,}/g, '-')
// 將 '-.' 替換成 '-0.'
val = val.replace(/^\./g, '0.').replace(/^\-\./, '-0.')
if (typeof binding.value !== 'undefined') {
// 期望保留的最大小數(shù)位數(shù)
let pointKeep = 0
if (typeof binding.value === 'string'
|| typeof binding.value === 'number') {
pointKeep = parseInt(binding.value)
+ } else if (typeof binding.value === 'object') {
+ // 支持新的小數(shù)點保留位配置
+ pointKeep = binding.value.decimal
}
if (!isNaN(pointKeep)) {
if (!Number.isInteger(pointKeep)
|| pointKeep < 0) {
pointKeep = 0
}
// 增加'-'號的支持
const str='^(\\-)*(\\d+)\\.(\\d\{' + pointKeep + '}).*$'
const reg=new RegExp(str)
if (pointKeep === 0) {
// 不需要小數(shù)點
val = val.replace(reg, '$1$2')
} else {
// 通過正則保留小數(shù)點后指定的位數(shù)
val = val.replace(reg, '$1$2.$3')
}
}
} else {
val = ele.value.replace(/[^\d]/g, '')
}
+ if (val !== '' && typeof binding.value === 'object') {
+ let { min, max } = binding.value
+ min = parseFloat(min)
+ max = parseFloat(max)
+ if (!isNaN(min)) {
+ if (min >= 0) {
+ // 不能是負數(shù)
+ val = val.replace('-', '')
+ }
+ if (parseFloat(val) < min) {
+ val = min
+ }
+ }
+ if (!isNaN(max)) {
+ if (parseFloat(val) > max) {
+ val = max
+ }
+ }
+ }
ele.value = val
}
使用方法:
// 最小為0
<el-input v-model="model" v-number-input="{ min: 0 }" />
結(jié)語
至此我們的功能已經(jīng)實現(xiàn)得差不多了,上述代碼已經(jīng)足以應(yīng)對我們的大多數(shù)的需求了。但在我發(fā)現(xiàn)某些場景下有的時候鍵入非數(shù)字時,表單驗證獲取的數(shù)值仍然是我鍵入的字符而非空值,導(dǎo)致非空驗證不正確。但是這種情況并非必現(xiàn)的,我猜想可能是因為是vue的model數(shù)據(jù)未同步導(dǎo)致的,因此在上述回調(diào)函數(shù)添加上以下代碼,手動觸發(fā)數(shù)據(jù)的雙向綁定。
if (vnode.componentInstance) {
vnode.componentInstance.$emit('input', ele.value)
} else {
vnode.elm.dispatchEvent(new CustomEvent('input', ele.value))
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
vue+scss+element-ui實現(xiàn)表格表頭斜杠一分為三方式
這篇文章主要介紹了vue+scss+element-ui實現(xiàn)表格表頭斜杠一分為三方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08
elementPlus中的Autocomplete彈出層錯位問題解決分析
這篇文章主要介紹了elementPlus中的Autocomplete彈出層錯位問題解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07
vue中使用Echarts?map圖實現(xiàn)下鉆至縣級的思路詳解
這篇文章主要介紹了vue中使用Echarts?map圖實現(xiàn)下鉆至縣級,需要注意的是,因為我是直接從?vue-cli2?直接跳到?vue-cli4?,還奇怪怎么讀取不到JSON,查找后才知道?vue-cli3?往后的項目基礎(chǔ)架構(gòu)對比舊版本有些區(qū)別,感興趣的朋友跟隨小編一起看看吧2022-01-01
vue實現(xiàn)點擊按鈕“查看詳情”彈窗展示詳情列表操作
這篇文章主要介紹了vue實現(xiàn)點擊按鈕“查看詳情”彈窗展示詳情列表操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
Vue如何實現(xiàn)el-table多選樣式變?yōu)閱芜x效果
這篇文章主要介紹了Vue如何實現(xiàn)el-table多選樣式變?yōu)閱芜x效果,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05

