当用户点击元素或使用键盘上的 Tab 键时,元素会获得焦点。还有一个 autofocus
HTML 属性,它可以在页面加载时默认将焦点放在元素上,以及其他获取焦点的方法。
聚焦元素通常意味着:“准备在此处接受数据”,因此这是我们可以运行代码以初始化所需功能的时刻。
失去焦点(“模糊”)的时刻可能更为重要。那是当用户单击其他位置或按 Tab 键转到下一个表单字段时,或者还有其他方法。
失去焦点通常意味着:“数据已输入”,因此我们可以运行代码来检查它,甚至可以将其保存到服务器等。
在处理焦点事件时有一些重要的特点。我们将尽力进一步介绍它们。
事件 focus/blur
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。此外,如果值正确,我们可以自动将更改后的值发送到服务器。
方法 focus/blur
方法 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 之外的所有浏览器中均有效(bug)。
如果我们在输入中输入内容,然后尝试使用 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…)