Composition實現科學文字計數器

很長的前言

前端時間須要作一個input組件,組件要求之一是:動態計算input文字並展現出來,大概以下:javascript

原本就是基於vue來作,想起來也是很簡單的事情,就是獲取$input.value.length~確實也是如此。 最初我是這麼寫的:css

<template>
    <div class="self-input">
        <input ref="input" type="text" class="self-input__inner" maxlength="maxlength" :input="handleInput" :value="currentValue" >
        <span class="self-input__calculator">
            <span class="self-input__calculator--current">{{ textCount }}</span>
            <span class="self-input__calculator--max">{{ maxlength }}</span>
        </span>    
    </div>
</template>
<script> export default { name: 'SelfInput', props: { value: { type: [String, Number] }, maxlength: [String, Number] }, data() { return { currentValue: this.value, textCount: 0 }; }, methods: { handleInput() { let inputVal = this.$refs.input.value; inputVal = inputVal.trim(); this.textCount = inputVal.length; this.$emit('input', inputVal); } } }; </script>
<style lang="scss"> .self-input{ $--calculator-width: 56px; &{ position: relative;display: inline-block; } &__inner{ padding-left: 5px;line-height: 28px;width: 220px;padding-right: $--calculator-width;font-size: 14px; } &__calculator{ &{ position: absolute;top: 0;right: 0;bottom: 0;display: inline-flex;justify-content: flex-end;align-items: center;padding-right: 5px;width: $--calculator-width;box-sizing: border-box; } &--current{ &::after{ content: "/";display: inline-block; } } &--max{ color: gray; } } } </style>
複製代碼

如你所見,在中文輸入時出現了非預期的文字數目計數(附上 Demo1 )~html

正文

看win下的效果就知道,在使用中文輸入的時候,直接使用input事件(v-model語法糖就是監聽input事件)就出現了非預期的問題!前端

在看ElementUI源碼的時候發現了一個之前沒有用過的事件(見識短淺),composition事件。這個事件有三個事件組成,分別是:compositionstartcompositionupdatecompositionendvue

compositionstart 事件觸發於一段文字的輸入以前(相似於 keydown 事件,可是該事件僅在若干可見字符的輸入以前,而這些可見字符的輸入可能須要一連串的鍵盤操做、語音識別或者點擊輸入法的備選詞)。java

compositionupdate 事件觸發於字符被輸入到一段文字的時候(這些可見字符的輸入可能須要一連串的鍵盤操做、語音識別或者點擊輸入法的備選詞)git

當文本段落的組成完成或取消時, compositionend 事件將被觸發 (具備特殊字符的觸發, 須要一系列鍵和其餘輸入, 如語音識別或移動中的字詞建議)。github

 

轉成凡人(說的就是本身)聽得懂的話就是:在一開始中文輸入的時候會觸發compositionstart事件,當繼續中文輸入但未選詞前會持續觸發compositionupdate事件,而後當選詞後則觸發compositionend事件(針對當前狀況如是說)!函數

用input事件配合以上三事件優化文字計數器

<input class="self-input__inner" type="text" :value="exValue" @compositionend="handleComposition" @input="handleInput" maxlength="maxlength" >
<!-- ... -->
<script> export default { // ... data() { return { // ... isOnComposition: false }; }, methods: { _setTextCount(val) { this.textCount = val.length || 0; }, handleComposition(event) { this.isOnComposition = event.type !== 'compositionend'; !this.isOnComposition && this.handleInput(event); }, handleInput(event) { let value; if (this.isOnComposition) return; value = event.target.value; this.currentValue = value; this._setTextCount(value); this.$emit('input', value); } } // ... } </script>
複製代碼

這裏只是用到了Composition事件之一, compositionend。其實要作得更加健壯應該三個事件都用到!這裏的思路也很簡單,若是是非中文輸入的時候就不會觸發compositionend事件,正常執行input事件的回調,而中文輸入的時候,compositionendinput都會同時觸發,在未完成選詞前,固然是不能執行input回到的真正邏輯,所以加入一個isOnComposition狀態記錄當前是否進行中文輸入,input回調函數則據此判斷是否執行真正的業務邏輯! 附上jsfiddle源碼Demo: Demo2 flex

更加簡單的作法

<!-- ... -->
<input class="self-input__inner" type="text" v-model="currentValue" :maxlength="maxlength" />
<!-- ... -->
<script> // ... data() { return { currentValue: (this.value === undefined || this.value === null) ? '' : this.value }; }, computed: { textCount() { return this.currentValue.length || 0; } } // ... </script>
複製代碼

對比上一種作法,將v-bind綁定的value屬性,換成v-model,其他去掉的部分一看到~,基本就是作了這麼一點功夫就搞定了上一種作法的長篇大論~ 附上jsfiddle源碼Demo: 另外一種簡單的實現

最後水一句

原本就不是經常使用的事件,寫個blog和小demo記錄下,省得下次…… 以爲寫得還能夠,移步Github Blog鼓勵下,哪怕一點點評論和like,讓我知道我寫的東西仍是有點用都是很大的激勵~

相關文章
相關標籤/搜索