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.
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>
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With