Vue中實(shí)現(xiàn)回車(chē)鍵切換焦點(diǎn)的方法
幾乎在所有瀏覽器中,都具有 Tab 鍵切換焦點(diǎn)的功能。
但是任性的用戶強(qiáng)烈要求一定要有 Enter 鍵切換焦點(diǎn)的功能。
為了交付上線拿到錢(qián),我們只好再一次毫無(wú)原則性的接受了客戶的需求。
在上一代人中,大多都有這種操作習(xí)慣。習(xí)慣把保存成為編輯,習(xí)慣用回車(chē)替換 Tab。這是受到微軟 excel 荼毒的結(jié)果。
起初我以為這個(gè)功能很簡(jiǎn)單,無(wú)非就是把 Enter 鍵的功能轉(zhuǎn)接到 Tab 鍵上面,分分鐘就可以解決掉的問(wèn)題。
可困難馬上就出現(xiàn)了,我發(fā)現(xiàn)這條路是走不通的。
我們經(jīng)??梢灾鲃?dòng)觸發(fā)某個(gè)事件,比如 el.click() 就可以調(diào)用點(diǎn)擊事件,或者使用 dispatchEvent 。但是鍵盤(pán)和鼠標(biāo)事件卻不行。
我查閱了很多資料,也做了很多嘗試。最后總結(jié)出來(lái)一個(gè)結(jié)論,在瀏覽器中,JavaScript 無(wú)法操作用戶的鍵盤(pán)或者鼠標(biāo),這是出于安全策略的考慮。仔細(xì)想一下,如果可以用一段 JavaScript 腳本控制用戶鍵盤(pán)和鼠標(biāo)的話,那么用戶只需要打開(kāi)一個(gè)黑客網(wǎng)站,黑客就可以瞬間得到他想得到的一切。
所以,如果要通過(guò)除 Tab 鍵以外的其他方式來(lái)觸發(fā)焦點(diǎn)切換, focus 幾乎是唯一的選擇。
在原生頁(yè)面中實(shí)現(xiàn)回車(chē)鍵切換焦點(diǎn)
項(xiàng)目是基于 vue 和 element-ui 做的,為了把實(shí)現(xiàn)思路先講清楚,暫時(shí)把這些拋開(kāi),從原生的頁(yè)面中尋找答案。
以下是一個(gè)原生的 html 頁(yè)面。
<!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>
接下來(lái)要實(shí)現(xiàn)通過(guò)回車(chē)鍵切換焦點(diǎn),我把思路梳理如下:
- 監(jiān)聽(tīng)回車(chē)鍵按下事件。
- 獲取當(dāng)前聚焦元素。
- 獲取下一個(gè)要被聚焦的元素。
- 切換焦點(diǎn)。
思路有了,實(shí)現(xiàn)起來(lái)也非常簡(jiǎn)單。
1.監(jiān)聽(tīng)回車(chē)鍵按下事件
在文檔中添加 script 標(biāo)簽,寫(xiě)入如下代碼。
function enterCallback(e) {
if (e.keyCode === 13) {
// 按下回車(chē)后的邏輯
}
}
window.addEventListener("keydown", enterCallback);
要注意, enterCallback 單獨(dú)拿出來(lái),用于注銷(xiāo)監(jiān)聽(tīng)事件。
監(jiān)聽(tīng)按鍵事件最常用的方法就是使用事件委托,將事件綁定到 window 對(duì)象上。相比較給每一個(gè)元素都綁定一個(gè)事件的方式,這樣做的最大好處就是節(jié)省內(nèi)存空間,性能更好。
判斷按下哪個(gè)鍵的方式有很多,比如判斷 e.key 、 e.code 或者 e.keyCode 等方式。但絕大多數(shù)的情況下都建議使用 e.keyCode 。下面是一張來(lái)自網(wǎng)絡(luò)的 keyCode 表。

2.獲取當(dāng)前聚焦元素
很容易就可以做到這一步。
常見(jiàn)的有兩種方式。第一種是 e.target ,第二種是 document.activeElement 。這種情況下,個(gè)人更推薦使用第二種。
function enterCallback(e) {
if (e.keyCode === 13) {
let activeEl = document.activeElement;
}
}
3.獲取下一個(gè)要被聚焦的元素
這一步也比較容易。使用 el.nextElementSibling API 即可獲取。
function enterCallback(e) {
if (e.keyCode === 13) {
let activeEl = document.activeElement;
let nextEl = activeEl.nextElementSibling;
}
}
4.切換焦點(diǎn)
切換焦點(diǎn)調(diào)用 focus 即可實(shí)現(xiàn)。
function enterCallback(e) {
if (e.keyCode === 13) {
let activeEl = document.activeElement;
let nextEl = activeEl.nextElementSibling;
nextEl && nextEl.focus();
}
}
至此一個(gè)最簡(jiǎn)單的 Demo 已經(jīng)實(shí)現(xiàn)了,接下來(lái)看看項(xiàng)目中實(shí)際的情況。
在 element-ui 項(xiàng)目中實(shí)現(xiàn)回車(chē)鍵切換焦點(diǎn)
因?yàn)槭鞘褂媒M件開(kāi)發(fā),加上樣式等因素,dom 節(jié)點(diǎn)并不像上面寫(xiě)的原生 Demo 那么簡(jiǎn)單,實(shí)際情況是多層嵌套的。下面是實(shí)際生成的代碼結(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é)議號(hào)</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ě)協(xié)議號(hào)" class="el-input__inner" /> <!----> </div> </div> </div>
可以看到,如果每一個(gè)輸入框都是這種類(lèi)型的嵌套結(jié)構(gòu),上面的方法是無(wú)法直接解決的。因?yàn)?nextElementSibling API 只能找到下一個(gè)兄弟元素,而在這里 input 明顯找不到下一個(gè)兄弟元素。
思路是,通過(guò)回溯的手段朝外層尋找,直到找到一個(gè)類(lèi)名包含 el-form-item 和 el-form-item--small 的祖級(jí)元素,然后再?gòu)倪@個(gè)祖級(jí)元素的下一個(gè)兄弟元素中尋找類(lèi)名包含 el-input__inner 的 input 元素。
所以要再寫(xiě)兩個(gè)函數(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;
}
有了這兩個(gè)函數(shù)以后,實(shí)現(xiàn)回車(chē)切換焦點(diǎn)就非常簡(jiǎn)單了。只需要執(zhí)行兩行代碼。
const container = findFormItem(document.activeElement); findInput(container) && findInput(container).focus();
完整的代碼大概是這樣的。
在 methods 中聲明三個(gè)方法。
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 中添加回車(chē)監(jiān)聽(tīng)和在 destroy 中移除回車(chē)鍵聽(tīng)。
mounted() {
this.addEnterListener();
},
destroy() {
this.removeEnterListener();
},
需要注意的是,項(xiàng)目是多標(biāo)簽頁(yè)的形式,表單組件可能會(huì)被渲染多次,所以通過(guò)在 window 對(duì)象上添加一個(gè) __completeEnterBind__ 字段來(lái)確?;剀?chē)換行事件正確綁定。
總結(jié)
以上所述是小編給大家介紹的Vue中實(shí)現(xiàn)回車(chē)鍵切換焦點(diǎn)的方法,希望對(duì)大家有所幫助,也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
vue項(xiàng)目input標(biāo)簽checkbox,change和click綁定事件的區(qū)別說(shuō)明
這篇文章主要介紹了vue項(xiàng)目input標(biāo)簽checkbox,change和click綁定事件的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
Vue3.0實(shí)現(xiàn)圖片預(yù)覽組件(媒體查看器)功能
最近項(xiàng)目中有個(gè)場(chǎng)景,一組圖片、視頻、音頻、文件數(shù)據(jù),要求點(diǎn)擊圖片可以放大預(yù)覽,左右可以切換音視頻、文件,支持鼠標(biāo)及各種鍵控制?縮放,左右旋轉(zhuǎn),移動(dòng)等功能,這篇文章主要介紹了Vue3.0實(shí)現(xiàn)圖片預(yù)覽組件(媒體查看器),需要的朋友可以參考下2023-12-12
關(guān)于vue項(xiàng)目一直出現(xiàn) sockjs-node/info?t=XX的解決辦法
sockjs-node 是一個(gè)JavaScript庫(kù),提供跨瀏覽器JavaScript的API,創(chuàng)建了一個(gè)低延遲、全雙工的瀏覽器和web服務(wù)器之間通信通道,這篇文章主要介紹了vue項(xiàng)目一直出現(xiàn) sockjs-node/info?t=XX的解決辦法,需要的朋友可以參考下2023-12-12
Windows系統(tǒng)下使用nginx部署vue2項(xiàng)目的全過(guò)程
nginx是一個(gè)高性能的HTTP和反向代理服務(wù)器,因此常用來(lái)做靜態(tài)資源服務(wù)器和后端的反向代理服務(wù)器,下面這篇文章主要給大家介紹了關(guān)于Windows系統(tǒng)下使用nginx部署vue2項(xiàng)目的相關(guān)資料,需要的朋友可以參考下2023-03-03
Vue.js中使用Vuex實(shí)現(xiàn)組件數(shù)據(jù)共享案例
這篇文章主要介紹了Vue.js中使用Vuex實(shí)現(xiàn)組件數(shù)據(jù)共享案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07
結(jié)合康熙選秀講解vue虛擬列表實(shí)現(xiàn)
這篇文章主要為大家介紹了結(jié)合康熙選秀講解vue虛擬列表的原理使用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
Vue3中使用pnpm搭建monorepo開(kāi)發(fā)環(huán)境
這篇文章主要為大家介紹了Vue3中使用pnpm搭建monorepo開(kāi)發(fā)環(huán)境示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08

