Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Position sticky not working for horizontal scrolling when element width increases

Tags:

html

css

I am trying to keep an element from scrolling past left: 0 using position: sticky. This works fine in some cases, but I have noticed that if the element width increases it stops working. For example, the following works:

#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>

But if I increase the witdth of header element to 100% it stops working.

#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>

Why does this happen? And is there any way to use position: sticky to prevent the header element from scrolling when it's width is 100%? I prefer not to use position: fixed in this case.

like image 982
jdnz Avatar asked Nov 30 '22 08:11

jdnz


2 Answers

I now understand what is happening. The issue is the different way the browser treats the width and height of a <div>. The default values of auto mean that the width of the <div> is 100% while the height is set by the content. If the content is wider than 100%, then on horizontal scroll the sticky element hits the end of the container <div> and, since it cannot leave the confines of the container, begins to scroll. This doesn't happen in the same situation for vertical scrolling since the container <div> is as tall as the content by default.

To prevent this happening, we have to ensure that the container <div> is as wide as its content. This can be done in most browsers (not Edge or Explorer) by including width: max-content in the container style. Alternatively, as proposed in mfluehr's answer, putting overflow: auto creates a new block formatting context that is as wide as the content. Another option is to use display: inline-block or inline-flex etc. to cause the container <div> to base its width on the content.

For example, using two of these techniques, you can create headers, sidebars and footers that stick for a page that can scroll vertically and horizontally:

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>
like image 119
jdnz Avatar answered Dec 28 '22 23:12

jdnz


This is an interesting problem. I don't know why, but putting overflow: auto on the container around the <div>s seems to fix the issue.

You can add height: 100vh to the container to let the content inside overflow with scrollbars.

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>
like image 23
mfluehr Avatar answered Dec 29 '22 00:12

mfluehr