Vue中實現(xiàn)回車鍵切換焦點的方法
幾乎在所有瀏覽器中,都具有 Tab 鍵切換焦點的功能。
但是任性的用戶強烈要求一定要有 Enter 鍵切換焦點的功能。
為了交付上線拿到錢,我們只好再一次毫無原則性的接受了客戶的需求。
在上一代人中,大多都有這種操作習(xí)慣。習(xí)慣把保存成為編輯,習(xí)慣用回車替換 Tab。這是受到微軟 excel 荼毒的結(jié)果。
起初我以為這個功能很簡單,無非就是把 Enter 鍵的功能轉(zhuǎn)接到 Tab 鍵上面,分分鐘就可以解決掉的問題。
可困難馬上就出現(xiàn)了,我發(fā)現(xiàn)這條路是走不通的。
我們經(jīng)??梢灾鲃佑|發(fā)某個事件,比如 el.click()
就可以調(diào)用點擊事件,或者使用 dispatchEvent
。但是鍵盤和鼠標(biāo)事件卻不行。
我查閱了很多資料,也做了很多嘗試。最后總結(jié)出來一個結(jié)論,在瀏覽器中,JavaScript 無法操作用戶的鍵盤或者鼠標(biāo),這是出于安全策略的考慮。仔細(xì)想一下,如果可以用一段 JavaScript 腳本控制用戶鍵盤和鼠標(biāo)的話,那么用戶只需要打開一個黑客網(wǎng)站,黑客就可以瞬間得到他想得到的一切。
所以,如果要通過除 Tab 鍵以外的其他方式來觸發(fā)焦點切換, focus
幾乎是唯一的選擇。
在原生頁面中實現(xiàn)回車鍵切換焦點
項目是基于 vue 和 element-ui 做的,為了把實現(xiàn)思路先講清楚,暫時把這些拋開,從原生的頁面中尋找答案。
以下是一個原生的 html 頁面。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>Demo</title> </head> <body> <form> <input placeholder="姓名" /> <input placeholder="性別" /> <input placeholder="年齡" /> </form> </body> </html>
接下來要實現(xiàn)通過回車鍵切換焦點,我把思路梳理如下:
- 監(jiān)聽回車鍵按下事件。
- 獲取當(dāng)前聚焦元素。
- 獲取下一個要被聚焦的元素。
- 切換焦點。
思路有了,實現(xiàn)起來也非常簡單。
1.監(jiān)聽回車鍵按下事件
在文檔中添加 script 標(biāo)簽,寫入如下代碼。
function enterCallback(e) { if (e.keyCode === 13) { // 按下回車后的邏輯 } } window.addEventListener("keydown", enterCallback);
要注意, enterCallback
單獨拿出來,用于注銷監(jiān)聽事件。
監(jiān)聽按鍵事件最常用的方法就是使用事件委托,將事件綁定到 window
對象上。相比較給每一個元素都綁定一個事件的方式,這樣做的最大好處就是節(jié)省內(nèi)存空間,性能更好。
判斷按下哪個鍵的方式有很多,比如判斷 e.key
、 e.code
或者 e.keyCode
等方式。但絕大多數(shù)的情況下都建議使用 e.keyCode
。下面是一張來自網(wǎng)絡(luò)的 keyCode
表。
2.獲取當(dāng)前聚焦元素
很容易就可以做到這一步。
常見的有兩種方式。第一種是 e.target
,第二種是 document.activeElement
。這種情況下,個人更推薦使用第二種。
function enterCallback(e) { if (e.keyCode === 13) { let activeEl = document.activeElement; } }
3.獲取下一個要被聚焦的元素
這一步也比較容易。使用 el.nextElementSibling
API 即可獲取。
function enterCallback(e) { if (e.keyCode === 13) { let activeEl = document.activeElement; let nextEl = activeEl.nextElementSibling; } }
4.切換焦點
切換焦點調(diào)用 focus
即可實現(xiàn)。
function enterCallback(e) { if (e.keyCode === 13) { let activeEl = document.activeElement; let nextEl = activeEl.nextElementSibling; nextEl && nextEl.focus(); } }
至此一個最簡單的 Demo 已經(jīng)實現(xiàn)了,接下來看看項目中實際的情況。
在 element-ui 項目中實現(xiàn)回車鍵切換焦點
因為是使用組件開發(fā),加上樣式等因素,dom 節(jié)點并不像上面寫的原生 Demo 那么簡單,實際情況是多層嵌套的。下面是實際生成的代碼結(jié)構(gòu)。
<div class="el-form-item el-form-item--small" style="margin-bottom: 0vh; width: 25%; display: inline-block;" > <label for="pactcode" class="el-form-item__label" style="width: 130px;" >協(xié)議號</label > <div class="el-form-item__content" style="margin-left: 130px;"> <div class="el-input el-input--small"> <!----> <input type="text" autocomplete="off" id="el-input" placeholder="未填寫協(xié)議號" class="el-input__inner" /> <!----> </div> </div> </div>
可以看到,如果每一個輸入框都是這種類型的嵌套結(jié)構(gòu),上面的方法是無法直接解決的。因為 nextElementSibling
API 只能找到下一個兄弟元素,而在這里 input 明顯找不到下一個兄弟元素。
思路是,通過回溯的手段朝外層尋找,直到找到一個類名包含 el-form-item
和 el-form-item--small
的祖級元素,然后再從這個祖級元素的下一個兄弟元素中尋找類名包含 el-input__inner
的 input 元素。
所以要再寫兩個函數(shù),分別是尋找組件元素的 findFormItem
和尋找 input 元素的 findInput
。
findFormItem:
function findFormItem(el) { const parent = el.parentElement; if (!parent) return document.body; if ( parent.className.includes("el-form-item") && parent.className.includes("el-form-item--small") ) { return parent; } return findFormItem(parent); }
findInput:
function findInput(container) { let nextEl = container.nextElementSibling; if (!nextEl) return; let input = nextEl.querySelector("input"); while (input.id === "el-select") { nextEl = nextEl.nextElementSibling; if (!nextEl) return; input = nextEl.querySelector("input"); } if (input.className.includes("el-input__inner")) return input; }
有了這兩個函數(shù)以后,實現(xiàn)回車切換焦點就非常簡單了。只需要執(zhí)行兩行代碼。
const container = findFormItem(document.activeElement); findInput(container) && findInput(container).focus();
完整的代碼大概是這樣的。
在 methods
中聲明三個方法。
methods: { addEnterListener() { if (window.__completeEnterBind__) return; window.addEventListener("keydown", this.enterCallback); window.__completeEnterBind__ = true; }, removeEnterListener() { window.removeEventListener("keydown", this.enterCallback); window.__completeEnterBind__ = false; }, enterCallback(e) { function findFormItem(el) { const parent = el.parentElement; if (!parent) return document.body; if ( parent.className.includes("el-form-item") && parent.className.includes("el-form-item--small") ) { return parent; } return findFormItem(parent); } function findInput(container) { let nextEl = container.nextElementSibling; if (!nextEl) return; let input = nextEl.querySelector("input"); while (input.id === "el-select") { nextEl = nextEl.nextElementSibling; if (!nextEl) return; input = nextEl.querySelector("input"); } if (input.className.includes("el-input__inner")) return input; } if (e.keyCode === 13) { const container = findFormItem(document.activeElement); findInput(container) && findInput(container).focus(); } } }
然后在 mounted
中添加回車監(jiān)聽和在 destroy
中移除回車鍵聽。
mounted() { this.addEnterListener(); }, destroy() { this.removeEnterListener(); },
需要注意的是,項目是多標(biāo)簽頁的形式,表單組件可能會被渲染多次,所以通過在 window 對象上添加一個 __completeEnterBind__
字段來確?;剀嚀Q行事件正確綁定。
總結(jié)
以上所述是小編給大家介紹的Vue中實現(xiàn)回車鍵切換焦點的方法,希望對大家有所幫助,也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
vue實現(xiàn)動態(tài)綁定行內(nèi)樣式style的backgroundImage
這篇文章主要介紹了vue實現(xiàn)動態(tài)綁定行內(nèi)樣式style的backgroundImage方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07vue2和vue3的v-if與v-for優(yōu)先級對比學(xué)習(xí)
這篇文章主要介紹了vue2和vue3的v-if與v-for優(yōu)先級對比學(xué)習(xí),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10Vue3報錯‘defineProps‘?is?not?defined的解決方法
最近工作中遇到vue3中使用defineProps中報錯,飄紅,所以這篇文章主要給大家介紹了關(guān)于Vue3報錯‘defineProps‘?is?not?defined的解決方法,需要的朋友可以參考下2023-01-01