在項目中,我們經常會遇到輸入框只允許輸入數字的情況,下面是一段自定義指定 代碼,復制到項目中,注冊指定即可使用
- 用法如下:
- 創建一個IntInput.js 文件,將下面代碼復制到文件中保存
- 在項目中的 main.js 文件中導入創建的 IntInput.js 文件
- 注冊全局指令:app.directive(‘int’, IntInput) ,注冊后即可使用 v-int 綁定元素(兼容了大多數組件庫中輸入框組件)
注意:以下代碼中的鉤子函數是 vue3 寫法,如需在 vue2 項目中使用,修改對應的鉤子函數即可(代碼中有注釋說明)
/*** 查找并返回元素內部的原生 <input> 節點。*/
function getInputElement(el) {if (el.tagName.toLowerCase() === 'input') {return el;}const input = el.querySelector('input');if (!input) {throw new Error('v-integer directive requires the element to contain an <input> tag.');}return input;
}/*** 將字符串中的全角數字轉換為半角數字。*/
function toHalfWidth(str) {return str.replace(/[\uff10-\uff19]/g, (char) => {return String.fromCharCode(char.charCodeAt(0) - 65248);});
}export default {/*** Vue 2: bind / Vue 3: mounted*/mounted(el) {const input = getInputElement(el);// ? 核心:引入一個狀態標志來跟蹤輸入法組合狀態el._v_integer_is_composing = false;const onCompositionStart = () => {el._v_integer_is_composing = true;};const onCompositionEnd = (event) => {// 組合結束后,標志位復原el._v_integer_is_composing = false;// ? 關鍵:手動觸發一次 input 事件(或直接調用處理函數)來執行清理// 使用 dispatchEvent 更符合事件流,并能被其他可能的監聽器捕獲event.target.dispatchEvent(new Event('input'));};// onKeydown 邏輯保持不變,用于攔截非組合狀態下的無效按鍵const onKeydown = (event) => {if (el._v_integer_is_composing) {return;}};const onInput = () => {// ? 關鍵:如果在組合狀態中,則不執行任何操作if (el._v_integer_is_composing) {return;}const originalValue = input.value;const normalizedValue = toHalfWidth(originalValue);const sanitizedValue = normalizedValue.replace(/[^\d]/g, '');if (originalValue !== sanitizedValue) {const selectionStart = input.selectionStart;const valueChange = originalValue.length - sanitizedValue.length;input.value = sanitizedValue;// 簡單恢復光標位置if (selectionStart) {input.selectionStart = input.selectionEnd = selectionStart - valueChange;}input.dispatchEvent(new Event('input', { bubbles: true }));}};// 存儲所有處理器以便解綁el._v_integer_handlers = {keydown: onKeydown,input: onInput,compositionstart: onCompositionStart,compositionend: onCompositionEnd,};// 綁定所有事件監聽器input.addEventListener('keydown', onKeydown);input.addEventListener('input', onInput);input.addEventListener('compositionstart', onCompositionStart);input.addEventListener('compositionend', onCompositionEnd);},/*** Vue 2: unbind / Vue 3: unmounted*/unmounted(el) {try {const input = getInputElement(el);if (el._v_integer_handlers) {input.removeEventListener('keydown', el._v_integer_handlers.keydown);input.removeEventListener('input', el._v_integer_handlers.input);input.removeEventListener('compositionstart', el._v_integer_handlers.compositionstart);input.removeEventListener('compositionend', el._v_integer_handlers.compositionend);delete el._v_integer_handlers;delete el._v_integer_is_composing;}} catch (e) {console.warn('Could not unbind v-integer listeners.', e);}}
};