JavaScript中alert后调用focus为何会循环触发onblur事件
blur事件无限触发alert的根本原因
逻辑推导和实际运行结果不一致,核心是两个对浏览器事件机制的假设不符合真实表现:
两个常见认知偏差
- blur事件的触发时机不是元素已经完成失焦之后,而是焦点开始转移、失焦流程尚未执行完毕的阶段,回调运行时原有的焦点转移流程是可被打断、挂起的
- alert/confirm/prompt这类原生模态弹窗存在特殊焦点策略:弹窗弹出时会强制抢占页面焦点,弹窗关闭时浏览器会默认将焦点返还给弹窗唤起前最后持有焦点的页面元素,该行为是浏览器内置逻辑,无需手动代码触发
无限循环的完整执行链路
常规逻辑推导的执行顺序是「输入框彻底失焦→触发blur回调→弹出alert→关闭弹窗→手动调用focus给输入框重设焦点→流程结束」,但浏览器实际执行顺序完全不同:
- 初始状态:输入框持有焦点
- 失焦动作触发,焦点开始从输入框向其他目标转移,blur事件回调
myFunction开始执行 - 代码运行至
alert()语句,浏览器同步弹出模态弹窗,强制将焦点抢占至弹窗本身,原本正在进行的失焦流程被挂起暂停 - 用户点击确定关闭弹窗,浏览器按内置逻辑自动将焦点返还给弹窗弹出前最后持焦的输入框
- 回调继续执行到
ele.focus()语句,此时输入框本身已经持有焦点,该行调用不会产生任何实际效果 myFunction回调全部执行完毕,之前被alert挂起的失焦流程恢复执行,焦点再次从输入框转移,第二次触发blur回调- 上述流程反复执行,最终表现为alert无限循环弹出
复现代码
<!DOCTYPE html> <html> <body> Input: <input type="text" name="fname" id="fname" onblur="myFunction(this)"> <p>The blur event fires when an element has lost focus.</p> <script> function myFunction(ele) { alert("element has been in blur status, that's why the function is triggered"); ele.focus(); } </script> </body> </html>
修复方案
要规避这个问题,要么避免在blur事件回调中使用会抢占焦点的原生弹窗方法,要么增加状态锁拦截重复触发,参考实现:
let blurLock = false; function myFunction(ele) { if (blurLock) return; blurLock = true; alert("element has been in blur status, that's why the function is triggered"); ele.focus(); blurLock = false; }
内容的提问来源于stack exchange,提问作者ildvzg68472
相关产品推荐
相关产品推荐

