Blur事件流与DOM更新同步问题:点击删除按钮后的异常行为排查
问题根源与解决方案
核心认知纠正
你对事件触发顺序的理解存在偏差:输入框的blur事件会优先于按钮的mousedown事件触发,这是浏览器的默认事件优先级规则,和ev.preventDefault()无关——preventDefault()仅能阻止按钮的默认行为(如获取焦点、表单提交),无法改变事件的触发时序。
异常行为完整梳理
- 输入框聚焦时点击删除按钮,浏览器先触发输入框
blur回调:- 此时容器元素仍在DOM树中,所以能正常获取并打印。
- 若
blur回调中执行container.remove(),容器会被从DOM树移除,但变量对该元素的内存引用依然存在。
- 随后触发按钮
mousedown回调:- 调用
ev.preventDefault()阻止按钮默认行为,但此时容器已被blur回调移除。 - 再次执行
container.remove()时,因元素已不在DOM树中,抛出DOMException。
- 调用
当你注释掉blur中的container.remove()时:
blur回调先执行,打印仍在DOM中的容器元素。mousedown回调后执行,移除容器,此时再次获取容器会返回null,因此打印null。
为什么已移除的元素还能被找到?
DOM元素从页面移除后,只是脱离了DOM树,但之前通过querySelector等方式获取的元素引用不会立即变为null——该引用会一直指向内存中的元素对象,直到没有变量引用它时才会被垃圾回收。因此即使元素已被移除,回调中的变量依然能访问到它。
解决方案
方案1:标记状态避免重复移除
给容器添加自定义属性标记,移除前检查状态:
// 输入框blur回调 input.addEventListener('blur', () => { const container = document.querySelector('.container'); if (container && !container.dataset.isRemoving) { container.dataset.isRemoving = 'true'; console.log(container); container.remove(); } }); // 删除按钮mousedown回调 button.addEventListener('mousedown', (ev) => { ev.preventDefault(); const container = document.querySelector('.container'); if (container && !container.dataset.isRemoving) { container.dataset.isRemoving = 'true'; console.log(container); container.remove(); } });
方案2:统一移除逻辑
封装单独的移除函数,确保只执行一次:
let isContainerRemoved = false; function removeContainer() { if (isContainerRemoved) return; const container = document.querySelector('.container'); if (container) { console.log(container); container.remove(); isContainerRemoved = true; } } input.addEventListener('blur', removeContainer); button.addEventListener('mousedown', (ev) => { ev.preventDefault(); removeContainer(); });
方案3:阻止按钮触发blur
通过relatedTarget判断失焦原因,点击按钮时跳过blur的移除逻辑:
input.addEventListener('blur', (ev) => { // 若失焦是因为点击删除按钮,不执行移除 if (ev.relatedTarget === button) return; const container = document.querySelector('.container'); console.log(container); container.remove(); }); button.addEventListener('mousedown', (ev) => { ev.preventDefault(); const container = document.querySelector('.container'); console.log(container); container.remove(); });
内容的提问来源于stack exchange,提问作者NickSm
相关产品推荐
相关产品推荐

