這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 問題 最近公司項目上遇到了掃碼槍因搜狗/微軟/百度/QQ等輸入法在中文狀態下,使用掃碼槍掃碼會丟失字元的問題 思考 這種情況是由於掃碼槍的硬體設備,在輸入的時候,是模擬用戶鍵盤的按鍵來實現的字元輸入的,所以會觸發輸入法的中文模式,並且也會 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
問題
最近公司項目上遇到了掃碼槍因搜狗/微軟/百度/QQ
等輸入法在中文狀態下,使用掃碼槍掃碼會丟失字元的問題
思考
這種情況是由於掃碼槍的硬體設備,在輸入的時候,是模擬用戶鍵盤的按鍵
來實現的字元輸入的,所以會觸發輸入法的中文模式,並且也會觸發輸入法的自動聯想。那我們可以針對這個來想解決方案。
方案一
首先想到的第一種方案是,監聽keydown
的鍵盤事件,創建一個字元串數組,將每一個輸入的字元進行比對,然後拼接字元串,並回填到輸入框中,下麵是代碼:
function onKeydownEvent(e) { this.code = this.code || '' const shiftKey = e.shiftKey const keyCode = e.code const key = e.key const arr = ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-'] this.nextTime = new Date().getTime() const timeSpace = this.nextTime - this.lastTime if (key === 'Process') { // 中文手動輸入 if (this.lastTime !== 0 && timeSpace <= 30) { for (const a of arr) { if (keyCode === 'Key' + a) { if (shiftKey) { this.code += a } else { this.code += a.toLowerCase() } this.lastTime = this.nextTime } else if (keyCode === 'Digit' + a) { this.code += String(a) this.lastTime = this.nextTime } } if (keyCode === 'Enter' && timeSpace <= 30) { if (String(this.code)) { // TODO dosomething.... } this.code = '' this.nextTime = 0 this.lastTime = 0 } } } else { if (arr.includes(key.toUpperCase())) { if (this.lastTime === 0 && timeSpace === this.nextTime) { this.code = key } else if (this.lastTime !== 0 && timeSpace <= 30) { // 30ms以內來區分是掃碼槍輸入,正常手動輸入時少於30ms的 this.code += key } this.lastTime = this.nextTime } else if (arr.includes(key)) { if (this.lastTime === 0 && timeSpace === this.nextTime) { this.code = key } else if (this.lastTime !== 0 && timeSpace <= 30) { this.code += String(key) } this.lastTime = this.nextTime } else if (keyCode === 'Enter' && timeSpace <= 30) { if (String(this.code)) { // TODO dosomething() } this.code = '' this.nextTime = 0 this.lastTime = 0 } else { this.lastTime = this.nextTime } } }
這種方案能解決部分問題,但是在不同的掃碼槍設備,以及不同輸入法的情況下,還是會出現丟失問題
方案二
使用input[type=password]
來相容不同輸入的中文模式,讓其只能輸入英文,從而解決丟失問題
這種方案網上也有不少的參考
# 解決中文狀態下掃描槍掃描錯誤
# input type=password 取消密碼提示框
使用password密碼框
確實能解決不同輸入法的問題,並且Focus
到輸入框,輸入法會被強制切換為英文模式
添加autocomplete="off"
或 autocomplete="new-password"
屬性
官方文檔: # 如何關閉表單自動填充
但是在Chromium內核
的瀏覽器,不支持autocomplete="off"
,並且還是會出現這種自動補全提示:
上面的屬性並沒有解決瀏覽器會出現密碼補全框,並且在輸入字元後,瀏覽器還會在右上角彈窗提示是否保存:
先解決密碼補全框,這裡我想到了一個屬性readonly
,input原生屬性。input[type=password]
在readonly
時,是不會有密碼補全的提示,並且也不會彈窗提示密碼保存。
那好,我們就可以在輸入前
以及輸入完成
後,將input[type=password]
立即設置成readonly
但是需要考慮下麵幾種情況:
- 獲取焦點/失去焦點時
- 當前輸入框已
focus
時,再次滑鼠點擊輸入框 - 掃碼槍輸出完成最後,輸入
Enter
鍵時,如果清空輸入框,這時候也會顯示自動補全 - 清空輸入框時
- 切換離開頁面時
這幾種情況都需要處理,將輸入框變成readonly
我用vue
+element-ui
實現了一份,貼上代碼:
<template> <div class="scanner-input"> <input class="input-password" :name="$attrs.name || 'one-time-code'" type="password" autocomplete="off" aria-autocomplete="inline" :value="$attrs.value" readonly @input="onPasswordInput"> <!-- <el-input ref="scannerInput" v-bind="$attrs" v-on="$listeners" @input="onInput"> --> <el-input ref="scannerInput" :class="{ 'input-text': true, 'input-text-focus': isFocus }" v-bind="$attrs" v-on="$listeners"> <template v-for="(_, name) in $slots" v-slot:[name]> <slot :name="name"></slot> </template> <!-- <slot slot="suffix" name="suffix"></slot> --> </el-input> </div> </template> <script> export default { name: 'WispathScannerInput', data() { return { isFocus: false } }, beforeDestroy() { this.$el.firstElementChild.setAttribute('readonly', true) this.$el.firstElementChild.removeEventListener('focus', this.onPasswordFocus) this.$el.firstElementChild.removeEventListener('blur', this.onPasswordBlur) this.$el.firstElementChild.removeEventListener('blur', this.onPasswordClick) this.$el.firstElementChild.removeEventListener('mousedown', this.onPasswordMouseDown) this.$el.firstElementChild.removeEventListener('keydown', this.oPasswordKeyDown) }, mounted() { this.$el.firstElementChild.addEventListener('focus', this.onPasswordFocus) this.$el.firstElementChild.addEventListener('blur', this.onPasswordBlur) this.$el.firstElementChild.addEventListener('click', this.onPasswordClick) this.$el.firstElementChild.addEventListener('mousedown', this.onPasswordMouseDown) this.$el.firstElementChild.addEventListener('keydown', this.oPasswordKeyDown) const entries = Object.entries(this.$refs.scannerInput) // 解決ref問題 for (const [key, value] of entries) { if (typeof value === 'function') { this[key] = value } } this['focus'] = this.$el.firstElementChild.focus.bind(this.$el.firstElementChild) }, methods: { onPasswordInput(ev) { this.$emit('input', ev.target.value) if (ev.target.value === '') { this.$el.firstElementChild.setAttribute('readonly', true) setTimeout(() => { this.$el.firstElementChild.removeAttribute('readonly') }) } }, onPasswordFocus(ev) { this.isFocus = true setTimeout(() => { this.$el.firstElementChild.removeAttribute('readonly') }) }, onPasswordBlur() { this.isFocus = false this.$el.firstElementChild.setAttribute('readonly', true) }, // 滑鼠點擊輸入框一瞬間,禁用輸入框 onPasswordMouseDown() { this.$el.firstElementChild.setAttribute('readonly', true) }, oPasswordKeyDown(ev) { // 判斷enter鍵 if (ev.key === 'Enter') { this.$el.firstElementChild.setAttribute('readonly', true) setTimeout(() => { this.$el.firstElementChild.removeAttribute('readonly') }) } }, // 點擊之後,延遲200ms後放開readonly,讓輸入框可以輸入 onPasswordClick() { if (this.isFocus) { this.$el.firstElementChild.setAttribute('readonly', true) setTimeout(() => { this.$el.firstElementChild.removeAttribute('readonly') }, 200) } }, onInput(_value) { this.$emit('input', _value) }, getList(value) { this.$emit('input', value) } // onChange(_value) { // this.$emit('change', _value) // } } } </script> <style lang="scss" scoped> .scanner-input { position: relative; height: 36px; width: 100%; display: inline-block; .input-password { width: 100%; height: 100%; border: none; outline: none; padding: 0 16px; font-size: 14px; letter-spacing: 3px; background: transparent; color: transparent; // caret-color: #484848; } .input-text { font-size: 14px; width: 100%; height: 100%; position: absolute; top: 0; left: 0; pointer-events: none; background-color: transparent; ::v-deep .el-input__inner { // background-color: transparent; padding: 0 16px; width: 100%; height: 100%; } } .input-text-focus { ::v-deep .el-input__inner { outline: none; border-color: #1c7af4; } } } </style>
至此,可以保證input[type=password]
不會再有密碼補全提示,並且也不會再切換頁面時,會彈出密碼保存彈窗。 但是有一個缺點,就是無法完美顯示游標
。如果用戶手動輸入和刪除,使用起來會有一定的影響。