setTimeout与event.key交互异常导致searchOverlay任意键关闭如何修复
问题根因
你现有代码的问题出在两个核心逻辑错误:
- 没有区分
keydown和click两种事件的判断逻辑,键盘事件触发时,你同时执行了event.target.closest判断,而如果此时焦点在搜索浮层本身而非内部.search-inner区域,该判断会返回true,导致任意按键都满足「或」条件触发关闭 - 把关闭条件判断放在了
setTimeout内部,且无论是否符合关闭条件,都先执行了searchOverlay.removeAttribute('style'),这也是你删除setTimeout后逻辑正常但动画失效的核心原因:你需要先判断是否满足关闭条件,满足的前提下再触发关闭动画,等动画执行完成后再修改全局类名。
修复方案
调整判断顺序,先区分事件类型做对应校验,符合关闭条件再执行动画相关操作,修改后的代码如下:
function searchClose(event) { // 先判断是否符合关闭条件,不符合直接退出 let shouldClose = false; // 键盘事件仅校验是否按了Escape if (event.type === 'keydown') { shouldClose = event.key === 'Escape'; } // 点击事件仅校验是否点在了.search-inner外部 if (event.type === 'click') { shouldClose = !event.target.closest(".search-inner"); } // 不符合关闭条件直接返回,不执行任何后续操作 if (!shouldClose) return; // 符合条件,先触发关闭动画 searchOverlay.removeAttribute('style'); // 等200ms动画执行完成后再移除打开状态类 setTimeout(function () { body.classList.remove('is-search-open'); event.stopPropagation(); }, 200); } searchOverlay.addEventListener('keydown', searchClose); searchOverlay.addEventListener('click', searchClose);
额外优化建议
如果你的动画是用CSS实现的,更稳妥的方式是监听transitionend事件而非写死200ms的延迟,避免动画时长调整后JS代码也要同步修改,示例逻辑如下:
// 仅在需要关闭时绑定一次动画结束事件 if (shouldClose) { searchOverlay.removeAttribute('style'); const onTransitionEnd = () => { body.classList.remove('is-search-open'); event.stopPropagation(); searchOverlay.removeEventListener('transitionend', onTransitionEnd); }; searchOverlay.addEventListener('transitionend', onTransitionEnd); }
内容的提问来源于stack exchange,提问作者Лео
相关产品推荐
相关产品推荐

