input輸入框大家應(yīng)該都很熟悉了吧,不知道大家有沒有遇到過這樣的一種情況:如上圖,在中文輸入過程中,輸入的拼音也會觸發(fā)input框的input事件,有些時(shí)候我們并不希望在中文輸入的過程中拼音觸發(fā)input事件。
在日常開發(fā)中,輸入框是一個(gè)常見的組件,但是在拼音輸入過程中,每次輸入都會觸發(fā)input事件,這可能會導(dǎo)致不必要的操作。本文將介紹一種解決該問題的Vue指令,使得在拼音輸入過程中不會觸發(fā)input事件,從而優(yōu)化輸入框體驗(yàn)。
我們需要了解compositionstart和compositionend兩個(gè)事件。當(dāng)用戶開始輸入拼音時(shí),會觸發(fā)compositionstart事件,此時(shí)輸入框的值并沒有真正改變。隨著輸入的進(jìn)行,最終會觸發(fā)compositionend事件,此時(shí)輸入框的值才會真正改變。我們可以通過監(jiān)聽這兩個(gè)事件,來控制何時(shí)觸發(fā)input事件。
具體實(shí)現(xiàn)方式是,在監(jiān)聽到compositionstart事件時(shí),將輸入框的值存儲下來,然后在監(jiān)聽到compositionend事件時(shí),再將輸入框的值與之前存儲的值進(jìn)行比較,如果不同,則觸發(fā)自定義的input事件,并將新值作為參數(shù)傳遞給監(jiān)聽器。
html復(fù)制代碼<div class="input-text-title">正常情況</div>
<div class="input-text-content">{{ inputText }}</div>
<input
placeholder="請輸入"
class="input-text"
@input="doInput"
id="inputContent"
/>
簡單定義一個(gè)輸入框,監(jiān)聽其input事件,inputText保存實(shí)時(shí)輸入內(nèi)容并展示到頁面上。
javascript復(fù)制代碼const inputContent=document.getElementById("inputContent");
inputContent.addEventListener("compositionstart", ()=> {
this.isComposing=true;
this.inputComplate(inputContent.value);
});
inputContent.addEventListener("compositionend", ()=> {
this.isComposing=false;
if (inputContent.value !==this.inputText) {
this.inputComplate(inputContent.value);
}
});
首先,我們通過getElementById方法獲取到id為"inputContent"的輸入框元素,并將其賦值給inputContent常量。
然后,我們給inputContent元素添加compositionstart事件的監(jiān)聽器。在事件觸發(fā)時(shí),會執(zhí)行回調(diào)函數(shù)。在回調(diào)函數(shù)中,我們將isComposing變量設(shè)置為true,表示正在進(jìn)行拼音輸入,然后調(diào)用inputComplate方法處理輸入框的值,并將輸入框的值作為參數(shù)傳遞給inputComplate方法。
接下來,我們給inputContent元素添加compositionend事件的監(jiān)聽器。在事件觸發(fā)時(shí),同樣會執(zhí)行回調(diào)函數(shù)。在回調(diào)函數(shù)中,我們將isComposing變量設(shè)置為false,表示拼音輸入結(jié)束,然后同樣調(diào)用inputComplate方法處理輸入框的值,并將輸入框的值作為參數(shù)傳遞給inputComplate方法。
通過監(jiān)聽這兩個(gè)事件,并在事件觸發(fā)時(shí)調(diào)用相應(yīng)的處理方法,我們可以實(shí)現(xiàn)在拼音輸入過程中控制input事件的觸發(fā)時(shí)機(jī),從而達(dá)到不觸發(fā)input事件的效果。
javascript復(fù)制代碼inputComplate(value) {
this.inputText=value;
},
doInput(event) {
if (this.isComposing) return;
const value=event.target.value;
this.inputComplate(value);
},
輸入框input事件綁定的是doInput方法,這個(gè)方法在這里只是用于過渡一下,在這里我們需要判斷當(dāng)前是否正在輸入拼音,如果是在輸入拼音的話我們就直接返回,不進(jìn)行后續(xù)操作,如果不是的話則調(diào)用inputComplate進(jìn)行后續(xù)處理,真正的處理邏輯應(yīng)該是在inputComplate方法中。
這樣我們就簡單的完成了一個(gè)忽略拼音輸入過程中的input事件的功能
本文到此結(jié)束
且慢,雖然上面這樣操作之后可以得到一個(gè)可以忽略拼音輸入過程中的input事件的輸入框;但是,如果我們有很多個(gè)輸入框需要加上這個(gè)功能呢?這樣的話我們每寫一個(gè)input框,想要加上這個(gè)功能的話不還得加一堆亂七八糟的代碼來處理,這明顯很不合理。
javascript復(fù)制代碼export default {
bind(el, binding) {
let isComposing=false;
let value="";
el.addEventListener("compositionstart", ()=> {
isComposing=true;
value=el.value;
});
el.addEventListener("compositionend", ()=> {
isComposing=false;
if (value !==el.value) {
binding.value(el.value);
}
});
// 添加自定義的input事件監(jiān)聽器
el.addEventListener("input", (event)=> {
if (isComposing) {
return;
}
const value=event.target.value;
// 處理輸入框的值
binding.value(value);
});
},
};
html復(fù)制代碼<input
placeholder="輸入試試"
class="input-text"
v-JIgnorePinyin="doInput1"
/>
在bind鉤子函數(shù)中,我們首先定義了兩個(gè)變量isComposing和value,分別用于記錄是否正在進(jìn)行拼音輸入和輸入框的值。
然后,我們給輸入框元素el添加compositionstart事件的監(jiān)聽器。在事件觸發(fā)時(shí),將isComposing設(shè)置為true,表示正在進(jìn)行拼音輸入,并將輸入框的值賦給value。
接下來,我們給輸入框元素el添加compositionend事件的監(jiān)聽器。在事件觸發(fā)時(shí),將isComposing設(shè)置為false,表示拼音輸入結(jié)束。然后,我們比較value和輸入框的當(dāng)前值el.value是否相等,如果不相等,則說明輸入框的值已經(jīng)發(fā)生了變化,此時(shí)我們調(diào)用binding.value方法,將當(dāng)前輸入框的值el.value作為參數(shù)傳遞給綁定該指令的處理函數(shù)。
最后,我們給輸入框元素el添加input事件的監(jiān)聽器。在事件觸發(fā)時(shí),首先判斷isComposing的值,如果為true,則直接返回,不做任何處理。如果isComposing的值為false,說明拼音輸入已經(jīng)完成,此時(shí)我們獲取輸入框的值event.target.value,并將其作為參數(shù)調(diào)用binding.value方法,從而處理輸入框的值。
通過定義這個(gè)自定義指令,我們可以在Vue組件中使用它來監(jiān)聽拼音輸入過程中的事件,并執(zhí)行相應(yīng)的處理邏輯,從而實(shí)現(xiàn)更加靈活的輸入框交互。
這樣的話后面需要加功能只需要一個(gè)指令即可搞定。
目前該組件也已經(jīng)收錄到我的組件庫,組件文檔地址如下: jyeontu.xyz/jvuewheel/#…
組件庫中還有許多好玩有趣的組件,如:
等等……
組件庫已開源到gitee,有興趣的也可以到這里看看:gitee.com/zheng_yongt…
覺得有幫助的可以點(diǎn)個(gè)star~
有什么問題或錯(cuò)誤可以指出,歡迎pr~
有什么想要實(shí)現(xiàn)的組件或想法可以聯(lián)系我~
作者:JYeontu
鏈接:https://juejin.cn/post/7330439494666223642
開發(fā)過程中,基本都遇到過需要限制輸入的情況,比如金額、僅字母數(shù)字、可輸入小數(shù)位等,網(wǎng)上搜了很多方法也遇到一些坑,所以分享出來。
在VUE中可以在v-modal后添加修飾符的形式來限制輸入,比如:
<input v-model.number="testValue" type="number">
.number可以實(shí)現(xiàn)限制數(shù)字輸入,但是會有以下問題:
/* 普通IE瀏覽器 樣式清除 */
input::-webkit-outer-spin-button,input::-webkit-inner-spin-button{
-webkit-appearance: none !important;
}
/* 火狐瀏覽器樣式清除 */
input[type="number"]{
-moz-appearance:textfield;
}
通過@input監(jiān)聽更新數(shù)據(jù),實(shí)現(xiàn)只能輸入數(shù)字,而且可以自行定制限制輸入內(nèi)容
<input v-model="testValue" @input="testValue = testValue.replace(/[^\d]/g,'')">
此方法可以滿足需求,但是無法封裝進(jìn)行批量使用
封裝input限制輸入指令
//input.js
const addListener = function(el, type, fn) {
el.addEventListener(type, fn, false)
}
//去掉空格
const spaceFilter = function(el) {
addListener(el, 'input', () => {
el.value = el.value.replace(/\s+/, '')
})
}
// 限制只能輸入整數(shù)和小數(shù)(價(jià)格類、最多兩位小數(shù))
const priceFilter = function(el) {
addListener(el, 'input', () => {
el.value = (el.value.match(/^\d*(\.?\d{0,2})/g)[0]) || null
if (isNaN(el.value)) {
el.value = ''
}
})
}
export default {
bind(el, binding) {
if (el.tagName.toLowerCase() !== 'input') {
el = el.getElementsByTagName('input')[0]
}
spaceFilter(el)
switch (binding.arg) {
case 'price':
priceFilter(el)
break
default:
console.warn('未知指令類型',binding.arg)
break
}
}
}
注冊全局自定義指令
//main.js
import inputFilter from '@/directives/InputFilter.js'
Vue.directive('inputFilter', inputFilter)
使用v-input-filter指令
<input v-modal="testValue" v-input-filter:price>
這樣封裝在使用時(shí)會出現(xiàn)一個(gè)隱蔽的bug,就是在輸入指令中正則限制以外的字符時(shí),視圖中輸入框顯示是正確的,但是在瀏覽器控制欄Vue Devtools中的testValue最后一位字符是最后輸入的時(shí)的字符。
比如輸入abc、123abc輸入框內(nèi)是 、123,但實(shí)際testValue值是c、123c。
原因是vue中綁定的值是通過監(jiān)聽input進(jìn)行賦值的,直接修改輸入框值不會觸發(fā)input事件,需要通過dispatchEvent再次手動觸發(fā)input事件,修改如下:
//input.js
···
// 防抖
let debounce = (fn, delay) => {
var delay = delay || 100;
var timer;
return function() {
var th = this;
var args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function() {
timer = null;
fn.apply(th, args);
}, delay);
};
}
···
// 限制只能輸入整數(shù)和小數(shù)(價(jià)格類、兩位小數(shù))
const priceFilter = function(el) {
addListener(el, 'input', debounce(() => {//添加防抖 防止反復(fù)觸發(fā)事件導(dǎo)致內(nèi)存溢出
el.value = (el.value.match(/^\d*(\.?\d{0,2})/g)[0]) || null
if (isNaN(el.value)) {
el.value = ''
}
//觸發(fā)input事件
el.dispatchEvent(new Event('input'))
}))
}
···
到這里算是滿足了要求,也能方便的使用,分享出來希望能夠拋磚引玉,學(xué)習(xí)到更好的方式,如果有更好的方法請告訴我,謝謝!
*請認(rèn)真填寫需求信息,我們會在24小時(shí)內(nèi)與您取得聯(lián)系。