表单焦点: focus/blur
当用户点击或按TAB键时元素会获得焦点。也有autofocus
Html属性可以设置元素获取默认焦点,当页面载入或其他方式。
获得焦点通常意味:准备在这里接受数据。所以此时我们可以写代码实现初始化或加载工作。
失去焦点时刻可能更重要(“blur”)。当用户在其他地方点击或按TAB键使焦点移动至下一个表单域,或其他方式。
失去焦点通常意味:数据已经进入。所以我们可以运行代码检查或保存数据至服务器等。
开发中使用焦点事件,有很多重要的特性。 本节详细进行说明。
focus/blur事件
事件focus
在获得焦点时被调用,事件blur
在失去焦点时被调用。 我们使用他们验证表单输入。
下面示例:
- blur处理检查是否在文本域中录入email,如果不是展示错误。
- 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提供很多input属性用于验证:required
,pattern
等。有时正好满足使用,Javascript可以实现更复杂的功能,如果检查正确,我们也可以自动发送改变后的数据至服务器。
focus/blur方法
方法elem.focus()
和 elem.blur()
设置元素获得或失去焦点.举例:如果值无效,则访问者无法立刻输入域:
<style>
.error {
background: red;
}
</style>
Your email please: <input type="email" id="input">
<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或点击其他表单域,则onblur
会再次让输入域获得焦点。
注意我们不能在onblur
调用event.preventDefault()
阻止失去焦点,因为onblur
在所有元素失去焦点之后执行。
Javascript初始化时失去焦点
失去焦点有很多情况。
很多时候时用户点击其他地方,但有时Javascript自身也会引起,如:
- alert弹出会获得焦点,所以会引起元素失去焦点(blur事件),当alert消失时,焦点会再次回来(focus事件)。
- 如果dom删除某元素,那么会触发失去焦点事件,如果后来恢复,却不会获得焦点。
这些特性有时会引起focus/blur
处理错误——因为不需要的触发。
当使用这些事件时需小心。如果想跟踪用户,初始化焦点-失去焦点,那么应该避免自身原因造成错误。
tabindex:允许任何元素获得焦点
默认情况下,很多元素不支持获得焦点。
浏览器之间有很多差异,但有一件事件总是正确的:用户交互元素支持focus/blur
事件,如:<button>, <input>, <select>, <a>
等。
另一方面,一些格式元素如<div>, <span>, <table>
默认不能获得焦点,方法elem.focus()
不工作,focus/blur
事件永远不会触发。
但可以通过tabindex
属性改变并实现。
该属性目的是指定当按tab键时,元素获得焦点的顺序。
如果有两个元素,第一个有tabindex="1"
,第二个有tabindex="2"
,焦点在第一个上,按tab键,焦点移动至第二个。
有两个特殊值:
tabindex="0"
使元素最后一个获得焦点.tabindex="-1"
移动tab键,忽略该元素.
如果有tabindex,任何元素支持获得焦点
举例,有一个list,点击第一项,然后按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 with 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
(0总是最后),正常情况,<li>
不支持获得焦点,但tabindex
让其可以,事件和样式一样。
使用Javascript代码实现elem.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>
上,焦点事件仅在input上,form不发出,所以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>
// put the handler on capturing phase (last argument true)
form.addEventListener("focusin", () => form.classList.add('focused'));
form.addEventListener("focusout", () => form.classList.remove('focused'));
</script>
总结
事件focus
、blur
触发元素获得/失去焦点事件。特别是:
- 如果不触发,可以使用捕获状态代替或
focusin/focusout
。 - 大多数元素缺省不支持焦点,使用
tabindex
使他们可以获得焦点。
当前焦点元素是有效的,通过document.activeElement
。