Sections

Following Sidebar

01 Dec 2022

Following Sidebar - виконується за допомогою position:sticky; та фальш-блоком над ним, для того щоб слідкувати за рухами користувача при скролі, якщо він вищий за сам viewport. Таку технологію використовують відомі компанії як Facebook, Twitter для того щоб не використовувати ще один скрол на проекті
HTML
SCSS
PostCSS
JS
                            <div class="container">
  <aside class="sidebar" id="sidebar">
    <div class="sidebar__buffer"></div>
    <div class="sidebar__inner">
      Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magni velit itaque necessitatibus, nemo molestiae, ipsam neque tempore voluptatibus quisquam veniam praesentium, maiores adipisci possimus quia quos? Commodi illum consectetur beatae?
    </div>
  </aside>
  <div class="main"></div>
</div>
                        
                            * {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.container {
  display: flex;
  justify-content: space-between;
  width: 89%;
  margin: 0 auto;
}

.sidebar {
  position: relative;
  width: min(250px, 50%);
  flex-shrink: 0;
  margin-right: 30px;
  height: auto;
}

.sidebar__inner {
  position: sticky;
  height: 450px;
  background: #ed145b;
  border-radius: 20px;
  padding: 30px;
}

.main {
  /*  margin === gap  */
  margin: 20px 0;
  height: 2000px;
  background: linear-gradient(180deg, rgba(131,58,180,1) 0%, rgba(253,29,29,1) 50%, rgba(252,176,69,1) 100%);
  width: calc(100% - min(250px, 50%) - 30px);
  border-radius: 20px;
}

@media screen and (max-width: 360px) {
  .container {
    flex-direction: column;
  }

  .sidebar {
    width: 100%;
    margin-bottom: 30px;
  }

  .sidebar__inner {
    position: relative;
    top: initial !important;
    bottom: initial !important;
  }

  .main {
    width: 100%;
  }
}
                        
                            * {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.container {
  display: flex;
  justify-content: space-between;
  width: 89%;
  margin: 0 auto;
}

.sidebar {
  position: relative;
  width: min(250px, 50%);
  flex-shring: 0;
  margin-right: 30px;
  height: auto;
}

.sidebar__inner {
  position: sticky;
  height: 450px;
  background: #ed145b;
  border-radius: 20px;
  padding: 30px;
}

.main {
  /*  margin === gap  */
  margin: 20px 0;
  height: 2000px;
  background: linear-gradient(180deg, rgba(131,58,180,1) 0%, rgba(253,29,29,1) 50%, rgba(252,176,69,1) 100%);
  width: calc(100% - min(250px, 50%) - 30px);
  border-radius: 20px;
}

@media screen and (max-width: 360px) {
  .container {
    flex-direction: column;
  }
  
  .sidebar {
    width: 100%;
    margin-bottom: 30px;
  }
  
  .sidebar__inner {
    position: relative;
    top: initial !important;
    bottom: initial !important;
  }
  
  .main {
    width: 100%;
  }
}
                        
                            function getPosition(element) {
  var xPosition = 0;
  var yPosition = 0;

  while (element) {
    xPosition += element.offsetLeft - element.scrollLeft + element.clientLeft;
    yPosition += element.offsetTop - element.scrollTop + element.clientTop;
    element = element.offsetParent;
  }

  return { x: xPosition, y: yPosition };
}

const contentSticky = document.getElementById("content-sticky");
const rightSidebar = document.getElementById("sidebar");
if (rightSidebar) {
    const sidebarBuffer = rightSidebar.querySelector(".sidebar__buffer");
    const sidebarInner = rightSidebar.querySelector(".sidebar__inner");
    // const headerHeight = +getRootStyle("--header-height").replace("px", "");
    const headerHeight = 0;

    // gap on top and bottom of sticky sidebar
    const gap = 20;

    let lastScrollTop = 0;

    // if flag true === scrolling down else scrolling up
    let flag = null;

    // if flagHeight true === sidebar is higher than viewport height
    let flagHeight =
        document.querySelector(".sidebar__inner").getBoundingClientRect()
            .height >
        window.innerHeight - headerHeight;

    function calcHeightOfSidebar(flag) {
        const contentStickyTop = getPosition(contentSticky).y;
        const sidebarInnerTop = getPosition(sidebarInner).y;
        sidebarBuffer.style.height = `${sidebarInnerTop - contentStickyTop}px`;

        if (flag) {
            // scroll up
            sidebarInner.style.top = "";
            sidebarInner.style.bottom = `${
                -headerHeight -
                gap +
                window.innerHeight -
                sidebarInner.getBoundingClientRect().height
            }px`;
        } else {
            // scroll down
            sidebarInner.style.bottom = "";
            sidebarInner.style.top = `${
                Math.min(
                    window.innerHeight -
                        sidebarInner.getBoundingClientRect().height,
                    headerHeight
                ) - gap
            }px`;
        }
        flagHeight =
            sidebarInner.getBoundingClientRect().height >
            window.innerHeight - headerHeight;
    }

    function resetSticky() {
        sidebarInner.style.bottom = "";
        sidebarInner.style.top = `${headerHeight + gap}px`;
        sidebarBuffer.style.height = "0";
    }

    if (!flagHeight) {
        resetSticky();
    } else {
        calcHeightOfSidebar(false);
    }

    window.addEventListener("scroll", function () {
        let st = window.pageYOffset || document.documentElement.scrollTop;

        if (!flagHeight) {
            resetSticky();
        }

        if (st > lastScrollTop && !flag) {
            // scroll down
            calcHeightOfSidebar(flag);
            flag = true;
        } else if (st < lastScrollTop && flag) {
            // scroll up
            calcHeightOfSidebar(flag);
            flag = false;
        }
        lastScrollTop = st <= 0 ? 0 : st;
    });

    window.addEventListener("resize", function (e) {
        if (!flagHeight) {
            resetSticky();
        }

        calcHeightOfSidebar(true);
        calcHeightOfSidebar(false);
    });
}
                        

Для того щоб зрозуміти як він саме працює, я створив codepen, де ви самі можете пощупати даний сайдбар (для того щоб зробуміти його роботу зменшіть viewport висоту, щоб вона була менша, ніж сам sidebar, і починайте скролити вверх-вниз)

Link: click

1