咱平时写网页交互,总盯着那些 “大功能”,比如怎么做弹窗、怎么搞表单校验,却很少在意一个藏在 DOM 里的 “小透明”:document.activeElement。
这玩意儿说穿了特简单:就是告诉你现在页面上 “用户正盯着操作的那个元素”。但你别小瞧它,之前我做项目时踩过好几个坑,最后发现,哦豁,用它一下就解决了!今天就跟你唠唠,这东西在实际场景里有多香,全是我踩坑总结的干货,特接地气~
先掰扯清楚:这玩意儿到底是啥?
其实不用讲复杂概念,你肯定遇过这种情况:比如你在输入框里打字,这时候输入框就是 “焦点元素”;按 Tab 键切到按钮上,按钮就成了焦点元素。而 document.activeElement 就是帮你 “抓包” 这个元素的 —— 你随时喊它一声,它就告诉你:“现在用户在跟哪个元素玩呢!”
就这么个小功能,能解决好多让用户 “想吐槽” 的问题,咱一个个说~
案例 1:填表单时,别让错误提示 “群魔乱舞”
你肯定有过这体验:填个注册表单,左边输手机号,右边邮箱还没填呢,俩输入框下面的错误提示全冒出来了,密密麻麻的看着就烦!
之前我做项目也踩过这坑,后来用 document.activeElement 改了下,瞬间清爽了。思路特简单:用户现在在输哪个框,就只校验哪个框,其他框的错误提示先藏起来。
比如表单里有手机号和邮箱俩输入框,代码可以这么写(咱别搞太复杂,看个意思就行):
// 给所有输入框加个“输入时校验”的监听
const inputs = document.querySelectorAll('input');
inputs.forEach(input => {
input.addEventListener('input', () => {
// 先把所有错误提示清空,别堆着
document.querySelectorAll('.error').forEach(el => el.textContent = '');
// 看看现在用户在输哪个框(这步就是核心!)
const currentInput = document.activeElement;
const errorEl = document.getElementById(`${currentInput.id}Error`);
// 只校验当前正在输的框
if (currentInput.id === 'phone') {
if (!/^\d{11}$/.test(currentInput.value) && currentInput.value) {
errorEl.textContent = '兄弟,手机号得11位啊!';
}
} else if (currentInput.id === 'email') {
if (!/^[\w-]+@[a-zA-Z0-9]+\.[a-zA-Z]+$/.test(currentInput.value) && currentInput.value) {
errorEl.textContent = '邮箱格式不对哦,比如xxx@xx.com';
}
}
});
});
你看,这样用户输手机号时,就只提示手机号的问题,输邮箱时才管邮箱 —— 再也不会被一堆错误提示烦到了,体验立马上去了~
案例 2:关弹窗后,别让焦点 “迷路”
这事儿你绝对遇过:点开一个弹窗(比如填个信息的小窗口),输完内容关了弹窗,想继续按 Tab 键切回之前的按钮,结果按了半天没反应,低头一看 —— 焦点不知道跑哪儿去了,默认回到了页面最开头。
我之前做后台系统时,用户就吐槽过这个:“关了弹窗想接着操作,结果得重新找按钮,太麻烦了!” 后来用 document.activeElement 记一下焦点位置,问题就解决了。
思路超简单:打开弹窗前,先记一下 “用户当时在操作哪个元素”;关弹窗时,再把焦点送回去。代码大概长这样:
<!-- 打开弹窗的按钮 -->
<button id="openModalBtn">点开我看弹窗</button>
<!-- 弹窗 -->
<div id="modal" style="display: none;">
<input type="text" placeholder="随便输点啥">
<button id="closeModalBtn">关掉弹窗</button>
</div>
const openBtn = document.getElementById('openModalBtn');
const closeBtn = document.getElementById('closeModalBtn');
const modal = document.getElementById('modal');
let lastFocusedEl; // 存一下打开弹窗前的焦点,像记笔记似的
// 点开弹窗时:先记焦点,再让弹窗里的输入框“接棒”
openBtn.addEventListener('click', () => {
lastFocusedEl = document.activeElement; // 记下来:用户之前在点这个按钮
modal.style.display = 'block';
// 让弹窗里的输入框获得焦点,用户不用再点一下
modal.querySelector('input').focus();
});
// 关掉弹窗时:把焦点送回“老家”
closeBtn.addEventListener('click', () => {
modal.style.display = 'none';
lastFocusedEl?.focus(); // 比如回到之前的“打开弹窗”按钮
});
你试试就知道,关了弹窗再按 Tab,焦点还在原来的位置,不用重新找,爽多了~
案例 3:编辑器按快捷键没反应?可能是焦点跑了
我之前做过一个简单的富文本编辑器,加了个 “Ctrl+B 加粗” 的功能。结果测试时发现:有时候按 Ctrl+B 有用,有时候没用,整得我一头雾水。
后来查了半天,哦!原来是焦点的问题 —— 比如用户点了编辑器旁边的按钮,焦点跑到按钮上了,这时候按 Ctrl+B,编辑器根本 “收不到” 这个信号,自然没反应。
后来用 document.activeElement 加了个判断,问题秒解:只有当焦点在编辑器里时,Ctrl+B 才生效。代码也特简单:
<!-- 可编辑的编辑器区域 -->
<div id="editor" contenteditable="true" style="border: 1px solid #ccc; padding: 10px;">
在这里输文字,按Ctrl+B加粗
</div>
<!-- 旁边的干扰按钮 -->
<button id="otherBtn">我是个没用的按钮</button>
const editor = document.getElementById('editor');
document.addEventListener('keydown', (e) => {
// 判断两个条件:1. 按的是Ctrl+B;2. 焦点在编辑器里
if (e.ctrlKey && e.key === 'b' && document.activeElement === editor) {
e.preventDefault(); // 别让浏览器自己弹书签栏(懂的都懂)
// 简单加粗逻辑:选中的文字包个<b>标签
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
const boldEl = document.createElement('b');
boldEl.appendChild(range.extractContents());
range.insertNode(boldEl);
}
}
});
现在就稳了 —— 用户想加粗时,只要焦点在编辑器里,按 Ctrl+B 就管用;焦点跑出去了,按了也不瞎反应,不会误操作~
案例 4:防止重复提交?让焦点 “锁” 在按钮上
你肯定遇过这种情况:点了 “登录” 按钮,网络有点慢,半天没反应,就忍不住又点了一下 —— 结果发了两次请求,搞不好还出现重复登录的 bug。
之前我处理这个问题,除了禁用按钮,还加了个小细节:点了提交后,让焦点留在按钮上。这样用户就算想点别的,也能直观感觉到 “正在处理中,别瞎点了”。
代码大概这样:
<input type="text" placeholder="用户名">
<input type="password" placeholder="密码">
<button id="submitBtn">登录</button>
const submitBtn = document.getElementById('submitBtn');
let isSubmitting = false; // 标记一下:是不是正在提交中
submitBtn.addEventListener('click', async () => {
if (isSubmitting) return; // 正在提交就别理了,防止点好几次
// 开始提交:按钮禁用,改文字,焦点锁在这里
isSubmitting = true;
submitBtn.disabled = true;
submitBtn.textContent = '登录中...';
submitBtn.focus(); // 焦点就放这,用户知道“这儿正在干活”
try {
// 模拟等2秒(比如发请求给后端)
await new Promise(resolve => setTimeout(resolve, 2000));
alert('登进去啦!');
} catch (err) {
alert('登错了,再试试~');
// 失败了就恢复按钮,让用户再点
submitBtn.disabled = false;
submitBtn.textContent = '重新登录';
} finally {
isSubmitting = false;
}
});
这样用户点了提交后,就算想点别的,焦点也在按钮上,心里有数 “哦,还在处理呢”,就不会瞎点了~
最后唠两句
其实 document.activeElement 这玩意儿,说穿了就是帮你 “搞懂用户现在在干嘛”。它不是什么高大上的功能,但能解决好多 “用户没说但心里吐槽” 的小问题。
比如你写交互时,多想想:用户现在的焦点在哪儿?会不会因为焦点不对,导致操作没反应?用它多做一步判断,网页体验立马就不一样了。
要是你之前也用过这玩意儿,或者有更骚的用法,评论区甩出来咱一起学!毕竟写代码这事儿,互相抄抄作业才进步快嘛~

8011

被折叠的 条评论
为什么被折叠?



