當使用者點選元素或使用鍵盤上的 Tab 鍵時,元素會接收焦點。另外,還有一個 autofocus
HTML 屬性,可在頁面載入時預設將焦點放在元素上,以及其他取得焦點的方法。
聚焦在元素上通常表示:「準備在此處接受資料」,因此這是我們可以執行程式碼以初始化所需功能的時機。
失去焦點(「模糊」)的時刻可能更重要。這是當使用者點選其他地方或按下 Tab 以前往下一個表單欄位時,或者還有其他方法。
失去焦點通常表示:「資料已輸入」,因此我們可以執行程式碼來檢查它,甚至將它儲存到伺服器等等。
在使用焦點事件時,有一些重要的特性。我們將盡力在後續說明它們。
焦點/模糊事件
focus
事件在聚焦時呼叫,而 blur
則在元素失去焦點時呼叫。
讓我們使用它們來驗證輸入欄位。
在以下範例中
blur
處理常式檢查欄位中是否已輸入電子郵件,如果沒有,則顯示錯誤。focus
處理常式隱藏錯誤訊息(在blur
中,它會再次檢查)
<style>
.invalid { border-color: red; }
#error { color: red }
</style>
Your email please: <input type="email" id="input">
<div id="error"></div>
<script>
input.onblur = function() {
if (!input.value.includes('@')) { // not email
input.classList.add('invalid');
error.innerHTML = 'Please enter a correct email.'
}
};
input.onfocus = function() {
if (this.classList.contains('invalid')) {
// remove the "error" indication, because the user wants to re-enter something
this.classList.remove('invalid');
error.innerHTML = "";
}
};
</script>
現代 HTML 允許我們使用輸入屬性進行許多驗證:required
、pattern
等。有時它們正是我們需要的。當我們需要更多彈性時,可以使用 JavaScript。如果值正確,我們也可以自動將變更的值傳送至伺服器。
焦點/模糊方法
方法 elem.focus()
和 elem.blur()
設定/取消元素的焦點。
例如,讓我們在值無效時,讓訪客無法離開輸入
<style>
.error {
background: red;
}
</style>
Your email please: <input type="email" id="input">
<input type="text" style="width:220px" placeholder="make email invalid and try to focus here">
<script>
input.onblur = function() {
if (!this.value.includes('@')) { // not email
// show the error
this.classList.add("error");
// ...and put the focus back
input.focus();
} else {
this.classList.remove("error");
}
};
</script>
它在所有瀏覽器中都能運作,唯 Firefox 除外(錯誤)。
如果我們在輸入中輸入一些內容,然後嘗試使用 Tab 或從 <input>
點擊離開,則 onblur
會將焦點返回。
請注意,我們無法在 onblur
中呼叫 event.preventDefault()
來「防止失去焦點」,因為 onblur
在元素失去焦點之後才會運作。
然而,在實務上,在實作此類功能之前,應該仔細思考,因為我們通常應該向使用者顯示錯誤,但不應阻止他們繼續填寫我們的表單。他們可能想要先填寫其他欄位。
焦點遺失可能發生於許多原因。
其中之一是當訪客點選其他地方時。但 JavaScript 本身也可能造成它,例如
alert
會將焦點移至自身,因此它會導致元素失去焦點(blur
事件),而當alert
被關閉時,焦點會回來(focus
事件)。- 如果從 DOM 中移除元素,也會導致焦點遺失。如果稍後重新插入它,則焦點不會回來。
這些功能有時會導致 focus/blur
處理常式行為不當,在不需要時觸發。
最佳做法是在使用這些事件時要小心。如果我們想要追蹤使用者引發的焦點遺失,則我們應避免自己造成它。
允許聚焦於任何元素:tabindex
預設情況下,許多元素不支援聚焦。
清單在瀏覽器之間略有不同,但有一件事始終正確:focus/blur
支援保證適用於訪客可以互動的元素:<button>
、<input>
、<select>
、<a>
等。
另一方面,用於格式化某些內容的元素,例如 <div>
、<span>
、<table>
,預設無法取得焦點。方法 elem.focus()
對這些元素無效,而且絕不會觸發 focus/blur
事件。
可以使用 HTML 屬性 tabindex
來變更這個設定。
任何元素只要有 tabindex
,就會變成可取得焦點。當使用 Tab(或類似功能)在這些元素之間切換時,屬性的值就是元素的順序號碼。
也就是說:如果我們有兩個元素,第一個的 tabindex="1"
,第二個的 tabindex="2"
,那麼在第一個元素時按下 Tab,焦點就會移到第二個元素。
切換順序為:具有 tabindex
從 1
以上的元素會優先(依據 tabindex
順序),然後才是沒有 tabindex
的元素(例如一般的 <input>
)。
沒有符合 tabindex
的元素會依據文件來源順序(預設順序)切換。
有兩個特殊值
-
tabindex="0"
會將元素置於沒有tabindex
的元素之間。也就是說,當我們切換元素時,具有tabindex=0
的元素會在具有tabindex ≥ 1
的元素之後。通常用於讓元素可取得焦點,但維持預設的切換順序。讓元素成為與
<input>
平行的表單一部分。 -
tabindex="-1"
只允許使用程式碼將焦點設定在元素上。Tab 鍵會忽略這些元素,但方法elem.focus()
有效。
例如,這裡有一個清單。按一下第一個項目,然後按下 Tab
Click the first item and press Tab. Keep track of the order. Please note that many subsequent Tabs can move the focus out of the iframe in the example.
<ul>
<li tabindex="1">One</li>
<li tabindex="0">Zero</li>
<li tabindex="2">Two</li>
<li tabindex="-1">Minus one</li>
</ul>
<style>
li { cursor: pointer; }
:focus { outline: 1px dashed green; }
</style>
順序如下:1 - 2 - 0
。一般來說,<li>
不支援取得焦點,但 tabindex
可以完全啟用這個功能,以及事件和使用 :focus
的樣式。
elem.tabIndex
也有效我們可以使用 elem.tabIndex
屬性,透過 JavaScript 新增 tabindex
。效果相同。
委派:focusin/focusout
事件 focus
和 blur
沒有冒泡效果。
例如,我們無法在 <form>
上放置 onfocus
以突顯它,如下所示
<!-- on focusing in the form -- add the class -->
<form onfocus="this.className='focused'">
<input type="text" name="name" value="Name">
<input type="text" name="surname" value="Surname">
</form>
<style> .focused { outline: 1px solid red; } </style>
上面的範例無法運作,因為當使用者將焦點放在 <input>
上時,focus
事件只會觸發在該輸入上。它不會冒泡。因此 form.onfocus
永遠不會觸發。
有兩種解決方案。
首先,有一個有趣的歷史功能:focus/blur
沒有冒泡,但會在擷取階段向下傳播。
這將會運作
<form id="form">
<input type="text" name="name" value="Name">
<input type="text" name="surname" value="Surname">
</form>
<style> .focused { outline: 1px solid red; } </style>
<script>
// put the handler on capturing phase (last argument true)
form.addEventListener("focus", () => form.classList.add('focused'), true);
form.addEventListener("blur", () => form.classList.remove('focused'), true);
</script>
其次,有 focusin
和 focusout
事件,與 focus/blur
完全相同,但它們會冒泡。
請注意,它們必須使用 elem.addEventListener
指派,而不是 on<event>
。
以下是另一個可運作的變體
<form id="form">
<input type="text" name="name" value="Name">
<input type="text" name="surname" value="Surname">
</form>
<style> .focused { outline: 1px solid red; } </style>
<script>
form.addEventListener("focusin", () => form.classList.add('focused'));
form.addEventListener("focusout", () => form.classList.remove('focused'));
</script>
摘要
事件 focus
和 blur
會在元素取得/失去焦點時觸發。
它們的特殊功能是
- 它們不會冒泡。可以改用擷取狀態或
focusin/focusout
。 - 大多數元素預設不支援焦點。使用
tabindex
使任何東西都可以取得焦點。
目前取得焦點的元素可用於 document.activeElement
。
留言
<code>
標籤;要插入多行程式碼,請將其包在<pre>
標籤中;要插入 10 行以上的程式碼,請使用沙盒 (plnkr、jsbin、codepen…)