Skip to content Skip to sidebar Skip to footer

Position Sticky: Scrollable, When Longer Than Viewport

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 'stuc

Solution 1:

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; 
}

functionscrollUpwards(stick_pos) {
  // If the element is already stuck to the top then we are fineif(stick_pos === 1) return stick_pos;
  // Figure out where the new window will be after scrolllet 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 topif (aboveAside){
    $("aside").css("position", "sticky")
      .css("top", 0)
      .css("bottom", '')
      .css("align-self", "flex-start");
    return1;
  }
  // 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 nothingif (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", "");
  return0;
}

functionscrollDownwards(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 fineif(stick_pos === -1) return stick_pos;
  // Figure out where the new window will be after scrolllet 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 nothingif (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", "");
  return0;
}
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#sectiondiv#nav-container {
  position: relative;
  display: flex;
  min-width: 2em;
}
div#sectiondiv#nav-containeraside {
  position: sticky;
  align-self: flex-start;
  /* begin: irrelevant styling */background-color: #81a4cd;
  color: white;
  text-align: center;
  width: 2em;
}
div#sectiondiv#nav-containerasidediv {
  padding: 0 .3em;
}
div#sectionarticle {
  margin-left: 0.5em;
}
div#sectionarticlep {
  margin: 0;
}
div#sectionarticlep + p {
  margin-top: 1.5em;
}
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><divid='section'><divid='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>

Solution 2:

There is a library called Sticky Sidebar which is made exactly for solving that problem.

Solution 3:

You can try to switch the anchor and position of the sticky element from top to bottom with jQuery depending on the scrolling direction and still use CSS's native position: sticky.

I made it work in this codepen but didn't have the time to smooth out the jumpiness when directions are changed. But maybe this is enough for you.

// Use display: flex on the containerwindow.onscroll = function (e) {
  if (window.scrollY < this.prevScrollY) {
    scrollUpwards();
  } else {
    scrollDownwards();
  }
  this.prevScrollY = window.scrollY; 
}

functionscrollUpwards() {
  $("aside").css("top", 0); 
  $("aside").css("bottom", '');
  $("aside").css("align-self", "flex-start");

}

functionscrollDownwards() {
  $("aside").css("top", ''); 
  $("aside").css("bottom", 0); 
  $("aside").css("align-self", "flex-end");
}

Solution 4:

Did you look into Scrollama

It uses the new Intersection Observer Web API which is basically browser telling JS when a certain element appears inside a viewport, without JS having to listen to scroll events. Therefore, useful for implementing position: sticky like behavior in JS in a performant way.

Post a Comment for "Position Sticky: Scrollable, When Longer Than Viewport"