为何移除绝对定位DOM时父元素高度未变却触发scroll事件
移除absolute定位元素时意外触发scroll事件的原因与解决方案
问题场景
- 存在一个
position: relative、高度5000px的父DOM; - 子元素为
position: absolute定位,高度小于父DOM; - 移除其中的item1时会触发scroll事件,示例代码中item1在5秒后被移除,终端会打印
scroll。
代码示例
JavaScript
const removeDom = () => { document.getElementById("remove").remove(); } const container = document.getElementById("container"); container.addEventListener("scroll", () => { console.log("scroll") }) container.scrollTop = 1000; setTimeout(() => { removeDom(); }, 5000)
CSS
.item { height: 100px; width: 100%; border: 1px solid black; }
HTML
<div id="container" style="height: 100vh; overflow: auto;"> <div style="height: 5000px; position: relative;"> <div style="position: absolute; top: 700px;"> <div id="remove" class="item">item1</div> <div class="item">item2</div> <div class="item">item3</div> <div class="item">item4</div> <div class="item">item5</div> <div class="item">item6</div> <div class="item">item7</div> <div class="item">item8</div> </div> </div> </div>
原因分析
这是浏览器的**滚动锚定(Scroll Anchoring)**机制在搞鬼:
- 当设置
container.scrollTop = 1000时,视口定位到容器内1000px的位置,此时item2及后续元素处于视口范围内; - 移除item1后,absolute定位的子容器高度减少100px,内部的item2及以下元素会自动向上偏移100px;
- 浏览器为了避免用户正在看的内容突然跳走,会自动调整
scrollTop的值来维持原视口的内容位置,这个调整操作就触发了scroll事件。
解决方案
1. 禁用滚动锚定(推荐)
给滚动容器添加overflow-anchor: none样式,直接阻止浏览器的自动scrollTop调整行为:
#container { overflow-anchor: none; }
或者直接在HTML内联样式中添加:
<div id="container" style="height: 100vh; overflow: auto; overflow-anchor: none;">
2. 临时取消scroll监听再恢复
如果不想全局禁用滚动锚定,可以在移除元素前后暂时移除scroll事件监听,操作完成后再恢复:
const scrollHandler = () => { console.log("scroll") } const removeDom = () => { // 先移除监听 container.removeEventListener("scroll", scrollHandler); document.getElementById("remove").remove(); // 异步恢复监听,避免触发不必要的scroll setTimeout(() => { container.addEventListener("scroll", scrollHandler); }, 0); } const container = document.getElementById("container"); container.addEventListener("scroll", scrollHandler); container.scrollTop = 1000; setTimeout(() => { removeDom(); }, 5000)
3. 用隐藏替代移除(业务允许的话)
如果不需要完全删除DOM元素,只是隐藏,可以设置visibility: hidden或opacity: 0,这样元素占据的空间不变,不会触发布局偏移和滚动锚定:
const removeDom = () => { document.getElementById("remove").style.visibility = "hidden"; }
内容的提问来源于stack exchange,提问作者GapMAX
相关产品推荐
相关产品推荐

