position:absolute元素在overflow:scroll容器中滚动时定位异常原因咨询
绝对定位元素在滚动容器中无法固定的原因及解决
问题场景
我给.wrapper设置了position:relative,.tag设置position:absolute,本以为这些标签能固定在.wrapper的视口角落,但滚动容器时标签会跟着内容一起滚动。我疑惑这是不是和overflow:scroll有关?.wrapper高度固定300px,滚动的是内部内容,容器本身没动,为什么top:0的标签没法留在顶部?
问题演示代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> * { padding: 0; margin: 0; box-sizing: border-box; } .wrapper { height: 300px; width: 500px; overflow: scroll; position: relative; } p { margin-bottom: 20px; } span.tag { position: absolute; padding: 10px; background-color: deepskyblue; color: white; } .tl { top: 0; left: 0; } .tr { top: 0; right: 0; } </style> </head> <body> <div class="wrapper"> <div class="content"> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit. </p> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit. </p> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit. </p> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit. </p> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit. </p> </div> <span class="tag tl">TopLeft</span> <span class="tag tr">TopRight</span> </div> </body> </html>
实现预期效果的代码(但不懂原理)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> * { padding: 0; margin: 0; box-sizing: border-box; } .wrapper { height: 300px; width: 500px; position: relative; } .newlyAdded { height: 100%; overflow: scroll; } p { margin-bottom: 20px; } span.tag { position: absolute; padding: 10px; background-color: deepskyblue; color: white; } .tl { top: 0; left: 0; } .tr { top: 0; right: 0; } </style> </head> <body> <div class="wrapper"> <div class="newlyAdded"> <div class="content"> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p> </div> <span class="tag tl">TopLeft</span> <span class="tag tr">TopRight</span> </div> </div> </body> </html>
原因解析
第一个代码的问题
你给.wrapper同时设置了position:relative和overflow:scroll,这个容器同时扮演了两个角色:绝对定位元素的参照父级,以及滚动区域的载体。
绝对定位的.tag虽然脱离了正常文档流,但它还是.wrapper的子元素,会被包含在.wrapper的滚动范围内。当你滚动容器时,浏览器会把.wrapper内部的所有内容(包括这些标签)整体平移,所以标签会跟着内容一起滚动。
第二个代码的原理
你新增的.newlyAdded把滚动功能从.wrapper里拆分出来了:
.newlyAdded单独作为滚动容器,它的滚动范围只包含自己的子元素(也就是.content);.tag仍然相对于.wrapper定位(因为.wrapper是position:relative,而.newlyAdded没设置定位属性),它不在.newlyAdded的滚动内容里,所以滚动.newlyAdded时,标签的位置不受影响,始终固定在.wrapper的角落。
关键总结
- 一个元素如果同时是定位上下文(设了
position:relative/absolute等)和滚动上下文(设了overflow:scroll/auto),内部的绝对定位元素会被纳入滚动范围,滚动时会跟着移动; - 要让绝对定位元素固定在容器角落,需要把定位上下文和滚动上下文拆分成两个不同的元素:外层做定位参照,内层做滚动容器。
内容的提问来源于stack exchange,提问作者Archsx
相关产品推荐
相关产品推荐

