Search code examples
javascripthtmlcsstypescripttailwind-css

Scrolling down makes div`s stick to top after eachother


I have multiple chapters (1, 2 and 3) and each chapter has text underneath them. When scrolling down, I need the chapters to stick to the top but one after eachother the more you scroll down. Each chapter has different heights (because of their content and also because of different screen sizes). How to make that work? I cannot figure out how to make the height variable and to stack the chapters one after eachother when scrolling down. top-0 should be top minus height of previous chapter but how to get that variable height of the previous chapter? Here the base code.

<div>
  <div className="sticky top-0 z-10 w-full bg-red-400">
    <h1>
      Chapter 1 div – big height big height big height big height big height big
      height
    </h1>
  </div>
  <p>
    Here text – here text - Here text – here textHere text – here textHere text
    – here textHere text – here text Here text – here text - Here text – here
    textHere text – here textHere text – here textHere text – here textHere text
    – here text - Here text – here textHere text – here textHere text – here
    textHere text – here textHere text – here text - Here text – here textHere
    text – here textHere text – here textHere text – here textHere text – here
    text - Here text – here textHere text – here textHere text – here textHere
    text – here text
  </p>
  <div className="sticky top-0 z-10 w-full bg-green-400">
    <h1>
      Chapter 2 div – biggest height biggest heightbiggest heightbiggest
      heightbiggest height heightbiggest heightbiggest heightheightbiggest
      heightbiggest heightheightbiggest heightbiggest height
    </h1>
  </div>
  <p>
    More words – more words - More words – more words - More words – more words
    - More words – more words - More words – more words - More words – more
    words -{" "}
  </p>
  <div className="sticky top-0 z-10 w-full bg-yellow-400">
    <h1>Chapter 3 div – small height </h1>
  </div>
  <p>
    Read this - Read this - Read this - Read this - Read this - Read this - Read
    this - Read this - Read this - Read this - Read this -{" "}
  </p>
</div>;

In this picture here you see my goal. The second pic on the right shows how it should look when having scrolled all the way down. So one-by-one the chapters stack horizontally.Picture of my goal


Solution

  • I'm not aware of any pure CSS solution to sequentially stack CSS position sticky Elements. You need to assign the top value in order to achieve that — JavaScript seems like the only way:

    const els = (sel, par) => (par || document).querySelectorAll(sel);
    
    const stickables = () => {
      els(".stickables").forEach(elGroup => {
        let prevHeight = 0;
        els(".sticky", elGroup).forEach((elSticky) => {
          if (prevHeight) {
            elSticky.style.setProperty("--y", prevHeight);
          }
          prevHeight += elSticky.clientHeight;
        });
      });
    };
    
    addEventListener("resize", stickables);
    addEventListener("load", stickables);
    stickables();
    * { margin:0; box-sizing: border-box; }
    .top-0 { top: 0; }
    .bg-red-400 { background: hsl(0 80% 50%); }
    .bg-green-400 { background: hsl(100 80% 50%); }
    .bg-yellow-400 { background: hsl(60 80% 50%); }
    
    
    .stickables {
      max-width: 500px;
      margin: 0 auto;
    }
    
    .sticky {
      position: sticky;
      top: calc(var(--y, 0) * 1px);
    }
    <div class="stickables">
      <div class="sticky bg-red-400">
        <h1>
          Chapter 1 div
        </h1>
      </div>
      <p style="height: 100vh;">Long text... scroll down...</p>
      <div class="sticky bg-green-400">
        <h1>
          Chapter 2 div – biggest height biggest heightbiggest heightbiggest heightbiggest height 
        </h1>
      </div>
      <p style="height: 100vh;">Long text... scroll down...</p>
      <div class="sticky bg-yellow-400">
        <h1>Chapter 3 div – small height </h1>
      </div>
      <p style="height: 100vh;">Long text... scroll down...</p>
    </div>

    Anyways this seems like you're looking for trouble. Someone on a narrow-height display - after all the headings stick below each other you're running the risk to not be able to read the content properly.
    If you don't take that into consideration... seems like a cool but failed approach to achieve something unusual :)