聚焦与失焦
焦点在什么时候到来、什么时候离开?
- 获得焦点: 用户点击控件、按
Tab、带autofocus属性的元素在页面加载时自动获得, 或 JS 调用focus(); - 失去焦点: 用户点别处、按
Tab跳走、或 JS 引起; - 语义: 聚焦 = "准备在这里输入数据", 是初始化的好时机; 失焦 = "数据已经输完", 是校验或保存的好时机;
- 典型用法: 失焦时校验, 重新聚焦时清掉上一次的错误提示;
input.onblur = function () {
if (!this.value.includes("@")) {
this.classList.add("invalid"); // 失焦: 报告问题
}
};
input.onfocus = function () {
this.classList.remove("invalid"); // 聚焦: 让用户重新输入
};
- 取舍:
required、pattern等输入属性已经能覆盖简单的校验, 只有需要更大灵活度时才写 JS; - 提醒: 一般"应该提示错误", 但不该阻止用户先填别的字段;
focus/blur 为什么做不了事件委托?
- 根本原因: 这两个事件不冒泡, 在
<form>上挂onfocus永远不会触发; - 方案一: 不冒泡但仍会向下经历捕获阶段, 用
addEventListener(type, handler, true)挂到外层即可;
form.addEventListener("focus", () => form.classList.add("focused"), true);
form.addEventListener("blur", () => form.classList.remove("focused"), true);
- 方案二: 换成语义相同的
focusin/focusout, 它们会冒泡;
form.addEventListener("focusin", () => form.classList.add("focused"));
form.addEventListener("focusout", () => form.classList.remove("focused"));
- 限制:
focusin/focusout只能用addEventListener注册, 没有onfocusin式的属性写法;
focus()/blur() 方法有哪些坑?
- 作用:
elem.focus()设置焦点,elem.blur()取消焦点; - 无法阻止失焦: 在
onblur里调用event.preventDefault()无效, 因为blur在焦点已经离开之后才触发; - 只能事后挽回: 在
onblur里再调input.focus()把焦点抢回来 (Firefox 不支持, 属于浏览器 bug);
input.onblur = function () {
if (!this.value.includes("@")) input.focus(); // 值不合法就拉回焦点
};
- JS 自身造成的失焦容易被忽略:
alert: 把焦点拉到自己身上, 于是元素失焦; 关闭后焦点又回来 (触发focus);- 元素被移出 DOM: 也造成失焦; 之后重新插入, 焦点不会自动回来;
- 后果:
focus/blur处理程序会在不该触发的时候触发; 想只跟踪"用户主动失焦", 就别自己制造失焦;
怎样让默认不可聚焦的元素变得可聚焦?
- 默认可聚焦: 用户能交互的元素, 如
<button>、<input>、<select>、<a>等; - 默认不可聚焦:
<div>、<span>、<table>这类纯排版元素, 对其调用focus()无效,focus/blur也永不触发; - 解法: 加
tabindex属性; 值即Tab切换时的次序号;
| 取值 | 含义 |
|---|---|
1、2 … | 正数排在最前, 按数值升序切换 |
0 | 排进"无 tabindex"那一组, 即排在所有正数之后, 保持文档顺序 |
-1 | Tab 跳过, 只允许用 focus() 程序化聚焦 |
不带 tabindex | 按文档源顺序切换 |
- 切换顺序示例:
<li tabindex="1">→<li tabindex="2">→<li tabindex="0">; - 附带效果: 加了
tabindex后该元素同时获得focus事件与:focus样式; - JS 等价操作:
elem.tabIndex = 0;
当前焦点落在哪个元素上?
document.activeElement: 读到当前聚焦的元素;