当元素宽度增加时,水平滚动时定位粘性不起作用

17

我正在尝试使用position: sticky使元素不会滚动超过left: 0。在某些情况下,这个方法可以很好地解决问题,但是当元素宽度增加时,这种方法就无法正常工作了。例如,以下内容可以正常工作:

#header {
  position: sticky;
  left: 0;
  width: 50%;
  background-color: #888;
}
#page {
  height: 80vh;
  width: 120vw;
  background-color: #000;
}
<div>
  <div id="header">
    Where is my mind?
  </div>
  <div id="page">
  </div>
</div>

但是如果我将头部元素宽度增加到100%,它就无法工作。

#header {
  position: sticky;
  left: 0;
  width: 100%;
  background-color: #888;
}
#page {
  height: 80vh;
  width: 120vw;
  background-color: #000;
}
<div>
  <div id="header">
    Where is my mind?
  </div>
  <div id="page">
  </div>
</div>

为什么会出现这种情况?有没有办法使用 position: sticky 来防止头部元素在其宽度为 100% 时滚动?我不想在这种情况下使用 position: fixed
2个回答

38

我现在明白发生了什么。问题出在浏览器以不同的方式处理 <div> 的宽度和高度上。 auto 的默认值意味着 <div> 的宽度为 100%,而高度由内容设置。如果内容比 100% 更宽,则在水平滚动时,粘性元素会撞到容器 <div> 的末端,并且由于它不能离开容器的限制而开始滚动。在垂直滚动的相同情况下,这种情况不会发生,因为容器 <div> 默认情况下与内容一样高。

为了防止这种情况发生,我们必须确保容器 <div> 与其内容一样宽。这可以在大多数浏览器中完成(不适用于Edge或Explorer),方法是在容器样式中包含 width: max-content。另一个选择是,如mfluehr的答案所提出的,加入 overflow: auto 可以创建一个新的块级格式化上下文,其宽度与内容一样。另一个选项是使用 display:inline-blockinline-flex 等使容器 <div> 基于内容来确定其宽度。

例如,使用这些技术中的两个,您可以为可以垂直和水平滚动的页面创建标题、侧边栏和页脚:

body {
  padding: 0;
  margin: 0;
}
#app {
  overflow: auto;
  height: 100vh;
}
#header {
  background: blue;
  width: 100%;
  height: 40px;
  position: sticky;
  top: 0;
  left: 0;
  z-index: 10;
  color: white;
}
#sidebar {
  position: sticky;
  background: green;
  width: 200px;
  height: calc(100vh - 40px);
  top: 40px;
  left: 0;
  color: white;
  flex-grow: 0;
  flex-shrink: 0;
}
#container {
  display: inline-flex;
}
#content {
  background: #555;
  height: 200vh;
  width: 200vw;
  background: linear-gradient(135deg, #cc2, #a37);
  flex-grow: 0;
  flex-shrink: 0;
}
#footer {
  background: #000;
  height: 100px;
  z-index: 100;
  left: 0;
  position: sticky;
  color: white;
}
<div id="app">
  <div id="header" ref="header">
    Header content
  </div>
  <div id="container">
    <div id="sidebar" ref="sidebar">
      Sidebar content
    </div>
    <div id="content" ref="content">
      Page content
    </div> 
  </div>
  <div id="footer" ref="footer">
    Footer content
  </div>  
</div>


3
哇,这个回答真的很棒。它真的有助于说明浏览器如何以不同的方式计算宽度和高度。我之前一直想不通为什么我的宽度没有扩展,但是高度却可以。这绝对让我有了更清晰的认识。谢谢! - Xenostar

8

这是一个有趣的问题。我不知道为什么,但在包围<div>的容器上加上overflow: auto似乎可以解决这个问题。

您可以在容器上添加height: 100vh,以便让内容溢出,并出现滚动条。

body {
  margin: 0;
}

#container {
  overflow: auto;
  height: 100vh;
}

#header {
  position: sticky;
  left: 0;
  width: 100%;
  background-color: #888;
}
#page {
  height: 200vh;
  width: 120vw;
  background: linear-gradient(135deg, #cc2, #a37);
}
<body>
  <div id="container">
    <div id="header">
      This is the header.
    </div>
    <div id="page">
      Page content goes here.
    </div>
  </div>
</body>


有趣而又奇怪的是,加上 overflow: auto 似乎会使垂直方向的粘性失效。我希望它能与 body 的水平滚动条一起工作,而不是让 div 有自己的滚动条(如果 div 比视口高,则可能出现在屏幕外)。 - jdnz
1
如果您在容器中添加 height: 100vh,我认为溢出滚动会按照您的要求工作。请查看我的编辑答案。 - mfluehr
很好,通过这个添加,在标题上加上 top: 0 也可以使其在垂直方向上固定。不过我有点困惑为什么会这样。 - jdnz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接