1. 程式人生 > 程式設計 >Vue中實現回車鍵切換焦點的方法

Vue中實現回車鍵切換焦點的方法

幾乎在所有瀏覽器中,都具有 Tab 鍵切換焦點的功能。

但是任性的使用者強烈要求一定要有 Enter 鍵切換焦點的功能。

為了交付上線拿到錢,我們只好再一次毫無原則性的接受了客戶的需求。

在上一代人中,大多都有這種操作習慣。習慣把儲存成為編輯,習慣用回車替換 Tab。這是受到微軟 excel 荼毒的結果。

起初我以為這個功能很簡單,無非就是把 Enter 鍵的功能轉接到 Tab 鍵上面,分分鐘就可以解決掉的問題。

可困難馬上就出現了,我發現這條路是走不通的。

我們經常可以主動觸發某個事件,比如 el.click() 就可以呼叫點選事件,或者使用 dispatchEvent 。但是鍵盤和滑鼠事件卻不行。

我查閱了很多資料,也做了很多嘗試。最後總結出來一個結論,在瀏覽器中,JavaScript 無法操作使用者的鍵盤或者滑鼠,這是出於安全策略的考慮。仔細想一下,如果可以用一段 JavaScript 指令碼控制使用者鍵盤和滑鼠的話,那麼使用者只需要開啟一個黑客網站,黑客就可以瞬間得到他想得到的一切。

所以,如果要通過除 Tab 鍵以外的其他方式來觸發焦點切換, focus 幾乎是唯一的選擇。

在原生頁面中實現回車鍵切換焦點

專案是基於 vue 和 element-ui 做的,為了把實現思路先講清楚,暫時把這些拋開,從原生的頁面中尋找答案。

以下是一個原生的 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>

接下來要實現通過回車鍵切換焦點,我把思路梳理如下:

  1. 監聽回車鍵按下事件。
  2. 獲取當前聚焦元素。
  3. 獲取下一個要被聚焦的元素。
  4. 切換焦點。

思路有了,實現起來也非常簡單。

1.監聽回車鍵按下事件

在文件中新增 script 標籤,寫入如下程式碼。

function enterCallback(e) {
 if (e.keyCode === 13) {
 // 按下回車後的邏輯
 }
}
window.addEventListener("keydown",enterCallback);

要注意, enterCallback 單獨拿出來,用於登出監聽事件。

監聽按鍵事件最常用的方法就是使用事件委託,將事件繫結到 window

物件上。相比較給每一個元素都繫結一個事件的方式,這樣做的最大好處就是節省記憶體空間,效能更好。

判斷按下哪個鍵的方式有很多,比如判斷 e.keye.code 或者 e.keyCode 等方式。但絕大多數的情況下都建議使用 e.keyCode 。下面是一張來自網路的 keyCode 表。

Vue中實現回車鍵切換焦點的方法

2.獲取當前聚焦元素

很容易就可以做到這一步。

常見的有兩種方式。第一種是 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.切換焦點

切換焦點呼叫 focus 即可實現。

function enterCallback(e) {
 if (e.keyCode === 13) {
 let activeEl = document.activeElement;
 let nextEl = activeEl.nextElementSibling;
 nextEl && nextEl.focus();
 }
}

至此一個最簡單的 Demo 已經實現了,接下來看看專案中實際的情況。

在 element-ui 專案中實現回車鍵切換焦點

因為是使用元件開發,加上樣式等因素,dom 節點並不像上面寫的原生 Demo 那麼簡單,實際情況是多層巢狀的。下面是實際生成的程式碼結構。

<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;"
 >協議號</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="未填寫協議號"
 class="el-input__inner"
 />
 <!---->
 </div>
 </div>
</div>

可以看到,如果每一個輸入框都是這種型別的巢狀結構,上面的方法是無法直接解決的。因為 nextElementSibling API 只能找到下一個兄弟元素,而在這裡 input 明顯找不到下一個兄弟元素。

思路是,通過回溯的手段朝外層尋找,直到找到一個類名包含 el-form-itemel-form-item--small 的祖級元素,然後再從這個祖級元素的下一個兄弟元素中尋找類名包含 el-input__inner 的 input 元素。

所以要再寫兩個函式,分別是尋找元件元素的 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;
}

有了這兩個函式以後,實現回車切換焦點就非常簡單了。只需要執行兩行程式碼。

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 中添加回車監聽和在 destroy 中移除回車鍵聽。

mounted() {
 this.addEnterListener();
},destroy() {
 this.removeEnterListener();
},

需要注意的是,專案是多標籤頁的形式,表單元件可能會被渲染多次,所以通過在 window 物件上新增一個 __completeEnterBind__ 欄位來確保回車換行事件正確繫結。

總結

以上所述是小編給大家介紹的Vue中實現回車鍵切換焦點的方法,希望對大家有所幫助,也非常感謝大家對我們網站的支援!