When element with position: sticky
is "stuck" and is longer than viewport, you can only see its content after you scroll to the bottom of container.
It would be cool, if the "stuck" element scrolled with the document and stopped, once it reaches its bottom edge. If user scrolled back, the same thing would happen again, but in reverse.
TLDR; There is a library (StickyKit) that does what I'm looking for, but performs badly with new async scrolling.
JSFiddle with StickyKit - https://jsfiddle.net/cibulka/4nd3b0tt/ - (this does what I'm describing, but performs poorly, see below)
JSFiddle with native position: sticky
- https://jsfiddle.net/cibulka/np6afe9c/1/ - https://jsfiddle.net/cibulka/pxz6e0qv/ - (this does not)
I was a happy user of StickyKit for a long time. Unfortunately, it does not work very well with asynchronous scrolling, which is employed by more and more browsers to improve performance. With the new Firefox Quantum (57), for example, StickyKit is pretty much unusable.
I've created an issue in StickyKit Github, but package seems to be abandonned by the author: https://github.com/leafo/sticky-kit/issues/252
Because of that, I am forced to deprecate StickyKit and move to native position:sticky
(polyfilled with StickyFill). Unfortunately there is a couple of things position:sticky
can't do and this is one of them.
There is also another issue I'm having with position:sticky
: Position sticky: overlay
A recommendation, basically, how to approach this issue. I'm up for using different JS/jQuery library, writing my own code or use some quirky CSS hack to hack position:sticky
functionality.
I've taken jnns answer and updated it so that it is smooth between scrolls like sticky kit may have been. The only issue is that it required a magic number for a containing div s.t. the container maintains it's size while the div is absolutely positioned - this might be solved in your code by a css variable.
window.onscroll = function (e) {
if (window.scrollY < this.prevScrollY) {
// Track position state of nav
// 1 == stuck to top
// 0 == absolute positioning
// -1 == stuck to bottom
this.stick_pos = scrollUpwards(this.stick_pos);
} else {
this.stick_pos = scrollDownwards(this.stick_pos);
}
this.prevScrollY = window.scrollY;
}
function scrollUpwards(stick_pos) {
// If the element is already stuck to the top then we are fine
if(stick_pos === 1) return stick_pos;
// Figure out where the new window will be after scroll
let aside = $("aside").get(0);
let aboveAside = aside.getBoundingClientRect().top > 0;
// If we are going above the element then we know we must stick
// it to the top
if (aboveAside){
$("aside").css("position", "sticky")
.css("top", 0)
.css("bottom", '')
.css("align-self", "flex-start");
return 1;
}
// If it will still be below the top of the element, then we
// must absolutely position it to its current position - if it already is absolutely positioned then we do nothing
if (stick_pos == 0) return stick_pos;
// Undo the stick to the bottom
// First get the current position
$("aside")
.css("top", aside.offsetTop)
.css("position", "absolute")
.css("bottom", '')
.css("align-self", "");
return 0;
}
function scrollDownwards(stick_pos) {
/*
let aside = $("aside").get(0);
let aboveAside = aside.offsetTop >= window.scrollY;
let browser_bottom = window.scrollY + window.innerHeight;
let aside_bottom = aside.offsetTop + aside.offsetHeight;
let belowAside = browser_bottom >= aside_bottom;
if (aboveAside) {
//console.log("stick to bottom");
$("aside").css("top", '');
$("aside").css("bottom", 0);
$("aside").css("align-self", "flex-end");
}
*/
// If the element is already stuck to the bottom then we are fine
if(stick_pos === -1) return stick_pos;
// Figure out where the new window will be after scroll
let aside = $("aside").get(0);
let browser_bottom = window.innerHeight;
let aside_bottom = aside.getBoundingClientRect().top + aside.offsetHeight;
let belowAside = browser_bottom > aside_bottom;
// If we are going below the element then we know we must stick
// it to the bottom.
if (belowAside){
$("aside").css("position", "sticky")
.css("top", '')
.css("bottom", 0)
.css("align-self", "flex-end");
return -1;
}
// If it will still be above the bottom of the element, then we
// must absolutely position it to its current position - if it already is absolutely positioned then we do nothing
if (stick_pos == 0) return stick_pos;
// Undo the stick to the top
// First get the current position
// $("aside").css("position", "absolute")
// .css("top", aside.offsetTop);
$("aside")
.css("top", aside.offsetTop)
.css("position", "absolute")
.css("bottom", '')
.css("align-self", "");
return 0;
}
div#section {
/* begin: irrelevant styling */
margin: 5em auto;
padding: 0.625rem;
max-width: 300px;
font-family: sans-serif;
font-size: 18px;
line-height: 1.5em;
text-align: justify;
background-color: #dbe4ee;
/* end: irrelevant styling */
display: flex;
justify-content: space-around;
}
div#section div#nav-container {
position: relative;
display: flex;
min-width: 2em;
}
div#section div#nav-container aside {
position: sticky;
align-self: flex-start;
/* begin: irrelevant styling */
background-color: #81a4cd;
color: white;
text-align: center;
width: 2em;
}
div#section div#nav-container aside div {
padding: 0 .3em;
}
div#section article {
margin-left: 0.5em;
}
div#section article p {
margin: 0;
}
div#section article p + p {
margin-top: 1.5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='section'>
<div id='nav-container'>
<aside>
<div>A</div>
<div>B</div>
<div>C</div>
<div>D</div>
<div>E</div>
<div>F</div>
<div>G</div>
<div>H</div>
<div>I</div>
<div>J</div>
<div>K</div>
<div>L</div>
<div>M</div>
<div>N</div>
<div>O</div>
<div>P</div>
<div>Q</div>
<div>R</div>
<div>S</div>
<div>T</div>
<div>U</div>
<div>V</div>
<div>W</div>
<div>X</div>
<div>Y</div>
<div>Z</div>
</aside>
</div>
<article>
<p>Perferendis ut iusto voluptatem ex temporibus aut autem amet. Sit vero in soluta. Est officia asperiores tenetur vel quam nostrum eum facere. Sed totam quasi libero at facilis doloremque. Non aut velit odio. Tempora dolore sunt recusandae sed quia
sunt.</p>
<p>Voluptatem optio asperiores dolorem voluptatem. Ipsa alias perspiciatis doloribus est nisi ut. Fuga aut et vitae consequatur dolor corrupti aut minima.</p>
<p>Facilis et ut eligendi. Excepturi labore asperiores vero. Perferendis porro sunt molestiae. In sit dolorem eum esse sit inventore est. Atque perspiciatis commodi nihil.</p>
<p>Consequatur ipsa id repellendus voluptatem perspiciatis temporibus. Praesentium eveniet nemo laudantium inventore similique impedit nihil esse. Maiores iste commodi molestiae quas odit nihil ex corrupti. Illum id amet non vero.</p>
<p>Voluptas soluta itaque et. Aperiam quasi sint eos ullam. Assumenda facilis omnis alias numquam. Odio quia esse vel et minima soluta architecto. Qui saepe consequatur aut rerum. Et et aut voluptatibus inventore.</p>
<p>Perferendis ut iusto voluptatem ex temporibus aut autem amet. Sit vero in soluta. Est officia asperiores tenetur vel quam nostrum eum facere. Sed totam quasi libero at facilis doloremque. Non aut velit odio. Tempora dolore sunt recusandae sed quia sunt.</p>
<p>Voluptatem optio asperiores dolorem voluptatem. Ipsa alias perspiciatis doloribus est nisi ut. Fuga aut et vitae consequatur dolor corrupti aut minima.</p>
<p>Facilis et ut eligendi. Excepturi labore asperiores vero. Perferendis porro sunt molestiae. In sit dolorem eum esse sit inventore est. Atque perspiciatis commodi nihil.</p>
<p>Consequatur ipsa id repellendus voluptatem perspiciatis temporibus. Praesentium eveniet nemo laudantium inventore similique impedit nihil esse. Maiores iste commodi molestiae quas odit nihil ex corrupti. Illum id amet non vero.</p>
<p>Voluptas soluta itaque et. Aperiam quasi sint eos ullam. Assumenda facilis omnis alias numquam. Odio quia esse vel et minima soluta architecto. Qui saepe consequatur aut rerum. Et et aut voluptatibus inventore.</p>
</div>
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