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