從一次輸入框無法輸入的bug,談如何限制輸入框輸入類型


bug的產生和修改

  上周臨近周末休息的時候,一個同事跑過來了,對我說:“阿倫啊,有一個頁面出問題了,火狐瀏覽器所有的input都沒法輸入了。”我一聽,是不是你給加了什么屬性,讓input輸入框只讀了啊。看了一下代碼,很正常的一個輸入框,並且CSS寫的也很正常。

<input id="ipt-message" type="text" placeholder="請輸入身高" />

  但是運行之后發現無法輸入任何東西,包括字母、符號、數字(后來實驗發現,輸入了漢字之后可以輸入符號和數字,這個暫時未發現原因)。那么問題就來了,肯定是js部分的問題了。當時我就猜大概是做了限制,但是當時我還是比較相信同事寫的代碼的。我就面對着幾個js文件一個一個的看,一個幾千行行代碼,知道我看到了下面這段代碼:

$("input[type='text']").keypress(function (e) {
    if (!String.fromCharCode(e.keyCode).match(/[0-9\.]/)) { return false; } }) 

  雖然當時沒驗證這段代碼的情況,但是直覺告訴我找到原因了。我把這段代碼復制出來還原了一下,大概意思就是所有的文本輸入框在keypress事件觸發這個函數,使用正則驗證輸入的情況,只能輸入小鼠和小數點。但是當運行的時候問題出現了,bug出現了,那么問題找到了,就是這四行代碼導致的,在那堆js代碼中去掉這四行之后就沒有了問題。

  之后我研究出錯的原因時發現,e.keyCode在谷歌時正常顯示,但是在火狐瀏覽器下就會出現問題了:

 

谷歌瀏覽器

火狐瀏覽器

IE11瀏覽器

按鍵“a”

keydown:keyCode為65,charCode為0

keypress:keyCode為97,charCode為97

keyup:keyCode為65,charCode為0

 keydown:keyCode為65,charCode為0

keypress:keyCode為0,charCode為97

keyup:keyCode為65,charCode為0

 keydown:keyCode為65,charCode為0

keypress:keyCode為97,charCode為97

keyup:keyCode為65,charCode為0

 按鍵“1”  

keydown:keyCode為49,charCode為0

keypress:keyCode為49,charCode為49

keyup:keyCode為49,charCode為0

 

keydown:keyCode為49,charCode為0

keypress:keyCode為0,charCode為49

keyup:keyCode為49,charCode為0

 

keydown:keyCode為49,charCode為0

keypress:keyCode為49,charCode為49

keyup:keyCode為49,charCode為0

 按鍵“Backspace”  

keydown:keyCode為8,charCode為0

keypress未觸發

keyup:keyCode為8,charCode為0

 

keydown:keyCode為8,charCode為0

keypress:keyCode為8,charCode為0

keyup:keyCode為8,charCode為0

 

keydown:keyCode為8,charCode為0

keypress為觸發

keyup:keyCode為8,charCode為0

  那么問題就找到原因了,通過String.fromCharCode(e.keyCode)是無法做到兼容火狐瀏覽器返回按鍵值,因為當輸入數字和字母時,其keyCode都為0。

  因此,我修改了一下這個代碼:

$("input[type='text']").keypress(function (e) {
    var code = e.charCode || e.originalEvent.charCode; if (code != 0) { if (!String.fromCharCode(code).match(/[0-9\.]/)) { return false; } } }) 

  originalEvent是jquery對原生event屬性的封裝。“code != 0”這個判斷是在火狐瀏覽器下對是否按鍵為“Backspace”的判斷,如果沒有這個判斷,會導致Backspace鍵無法使用,無法刪除這個情況的發生。

談限制輸入框輸入類型

  其實無論是使用哪種方式來限制輸入框的輸入的類型,都離不開keyup、keypress、keyup和比較少見的textInput四個事件來觸發。其中,前三個為各個瀏覽器共同支持的,而textInput僅有IE9+,Safari和Chrome,這也正式比較常見的瀏覽器(或其內核)。前三個事件為鍵盤事件,最后一個為文本事件。其觸發的順序為keydown(按鍵按下)——>keypress(按鍵值插入文本)——>textInput(按鍵值插入文本)——>keyup(按鍵彈起)。textInput和keypress的發生很相似,但二者還是有區別:任何可以獲得焦點的元素都可以觸發keypress事件,但只有可編輯區域才能觸發textInput事件。textInput事件只會在用戶按下能夠輸入實際字符的鍵時才會觸發,而keypress事件則在按下那些能夠影響文本顯示的鍵時也會觸發(比如退格鍵)。

  在實際的操作中,我們會發現在輸入中文的時候,只用按鍵事件對其進行觸發限制輸入框的操作體驗非常不好。比如上面的代碼,只在keypress事件觸發限制,當我們輸入法切換在中文時,按下字母之后按空格鍵或者“shift”鍵,會發現我們的限制失效了。

原生js

  因此為了更好的體驗,我們需要更多的事件觸發限制,達到我們的目的。因此,我們應在觸發keypress后在對即將插入文本框的值進行一邊過濾。就拿上文的條件只能輸入數字和小數點來說,我們需要在textIput事件發生時判斷一下文本框的value值,使用正則進行一下過濾。代碼如下:

    var EventUtil = {
        addHandler: function (element, type, handler) { if (element.addEventListener) { //DOM2級 element.addEventListener(type, handler, false); } else if (element.attachEvent) { //DOM1級 element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; //DOM0級  } }, removeHandler: function (element, type, handler) { //類似addHandler if (element.removeEventListener) { element.removeEventListener(type, handler, false); } else if (element.detachEvent) { element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } } } var textbox = document.getElementById('input'); EventUtil.addHandler(textbox, 'textInput', function (e) { e.target.value = e.target.value.replace(/[^0-9\.]/g, '') })

  利用事件監聽把綁定textIput事件,當觸發時再對其value過濾一下。結果還是不錯的:

  但是在上文提到過,textInput屬性對火狐瀏覽器無兼容,因此,我們就需要使用keyup對其進行代替(但效果不好):

textbox.onkeyup=function (e) {
    e.target.value = e.target.value.replace(/[^0-9\.]/g, '') }

  附完整代碼,原生js實現:代碼地址

Vue的自定義指令

  簡單少量的input我們可以使用雙向綁定后,watch到變化進行限制,如下:

<input id="ipt" type="text" v-model="iptVal"/>
<script>
    var qwe=new Vue({ el:'#ipt', data(){ return{ iptVal:'' } }, watch:{ iptVal(val){ this.iptVal=val.replace(/[^0-9\.]/g, '') } } }) </script>

  但是,當面對大量的input輸入框,我們更向往用簡單的方式去解決,甚至簡單到只寫一個指令(比如v-limit)就可以。這樣,我們就需要用到自定義指令的知識(可參考我的博客《Vue的土著指令和自定義指令》)。

  這個value是隨着輸入不斷更新的,因此,我們需要選用update這個鈎子函數:

    Vue.directive('limit', {
        update: function (el) { el.onkeypress = function (e) { var code = e.charCode; if (code != 0) { if (!String.fromCharCode(code).match(/[0-9\.]/)) { return false; } } } el.addEventListener('textInput', function (e) { e.target.value = e.target.value.replace(/[^0-9\.]/g, '') }) el.onkeyup = function (e) { e.target.value = e.target.value.replace(/[^0-9\.]/g, '') } } })

  此時,調用的方式特別簡單,只需要增加“v-limit”這個指令即可。

<input id="ipt" type="text" v-model="iptVal" v-limit />

  附完整代碼,基於Vue的自定義指令的實現:代碼地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM