Sticky Element – JavaScript & CSS

Sticky Element – JavaScript & CSS

Halo semua, 2 bulan ini absen posting dikarenakan kesibukan yang padat. Mari kita mulai belajar lagi seputar JavaScript dan CSS, kali ini kita akan membahas dan membuat sticky element. Sticky element paling sering kita temukan pada navigation bar sebuah website, ada banyak cara untuk membuat sticky element, pada postingan kali ini kita akan membuat dengan 2 cara berbeda. Langsung saja kita buat file index.html, app.js dan style.css

index.html

<!DOCTYPE html>
<html lang=“en”>
<head>
<meta charset=“UTF-8” />
<meta name=“viewport” content=“width=device-width, initial-scale=1.0” />
<title>Sticky Navigation</title>
<link rel=“stylesheet” href=“style.css” />
</head>
<body>
<header>
<div class=“nav”>
<div class=“logo”>LOGO</div>
<ul class=“menu”>
<li><a href=“#” class=“section-1”>Section 1</a></li>
<li><a href=“#” class=“section-2”>Section 2</a></li>
<li><a href=“#” class=“section-3”>Section 3</a></li>
<li><a href=“#” class=“section-4”>Section 4</a></li>
</ul>
</div>
<div class=“title”>
<h1>This is header</h1>
</div>
</header>
<section class=“section” id=“section-1”>This is section 1</section>
<section class=“section” id=“section-2”>This is section 2</section>
<section class=“section” id=“section-3”>This is section 3</section>
<section class=“section” id=“section-4”>This is section 4</section>

<script src=“app.js”></script>
</body>
</html>

style.css

/* file: style.css */

* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

.title {
height: 900px;
background-color: cornflowerblue;
}

.nav {
height: 50px;
width: 100%;
background-color: aqua;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: bold;
}

.nav .logo {
padding-left: 20px;
}

.nav ul {
display: flex;
}

.nav ul li {
list-style: none;
padding-right: 20px;
}

.nav ul li a {
text-decoration: none;
}

.section {
height: 900px;
font-size: 50px;
}

.section:nth-child(odd) {
background-color: greenyellow;
}

.section:nth-child(even) {
background-color: burlywood;
}

.nav.sticky {
position: fixed;
}

Sticky Element Cara Pertama

Kita isi dulu file app.js dengan implementasi smooth scrolling pada materi yang pernah kita bahas disini

// file: app.js

const selectors = {
linkSection1: document.querySelector(.section-1),
linkSection2: document.querySelector(.section-2),
linkSection3: document.querySelector(.section-3),
linkSection4: document.querySelector(.section-4),
section1: document.querySelector(#section-1),
section2: document.querySelector(#section-2),
section3: document.querySelector(#section-3),
section4: document.querySelector(#section-4),
navbar: document.querySelector(.nav),
};

// Smooth scrolling
selectors.linkSection1.addEventListener(click, () => {
selectors.section1.scrollIntoView({ behavior: smooth });
});
selectors.linkSection2.addEventListener(click, () => {
selectors.section2.scrollIntoView({ behavior: smooth });
});
selectors.linkSection3.addEventListener(click, () => {
selectors.section3.scrollIntoView({ behavior: smooth });
});
selectors.linkSection4.addEventListener(click, () => {
selectors.section4.scrollIntoView({ behavior: smooth });
});

Hasilnya kurang lebih sebagai berikut

Kita ingin pada saat masuk ke Section 1 dan seterusnya, navigation bar-nya muncul. Kurang lebih logic-nya sebagai berikut :

Dapatkan koordinat atas (top) Section 1 terhadap koordinat window
Jika koordinat window lebih dari koordinat atas (top) Section 1 maka munculkan navigation bar

Mari kita implementasikan pada file app.js

// Sticky navbar cara pertama
// 1. Sticky navigation muncul pada saat section 2 tampil, maka harus kita dapatkan koordinat Y section 2
const section1Coord = selectors.section1.getBoundingClientRect();
console.log(Section 1 top:, section1Coord.top);

// 2.Jika tampilan koordinat Y window lebih dari koordinat Y section 1 maka sticky nav muncul
window.addEventListener(scroll, () => {
console.log(window.scrollY);
if (window.scrollY > section1Coord.top) selectors.navbar.classList.add(sticky);
else selectors.navbar.classList.remove(sticky);
});

Jika kita inspect halaman, berapa sih koordinat atas (top) section 1 sebenarnya ? Dari kode diatas didapatkan bahwa jarak atas (top) Section 1 terhadap window adalah 950 pixel. Hal ini sebenarnya bisa kita hitung dari tinggi header (900 px) + tinggi navigation bar (50 px).

Maka dengan logic diatas jika window scroll lebih dari 950 pixel maka navigation bar akan muncul, hasilnya sebagai berikut

Metode ini tidak cocok dipakai untuk skala besar karena scroll akan aktif terus menerus selama halaman diakses, dapat menyebabkan performance issue.

Sticky Element Dengan Intersection Observer API

Apa pula itu Intersection Observer API ? Berikut penjelasan dari website MDN

The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document’s viewport.

Penjelasan singkatnya adalah intersection observer API akan memonitor posisi target terhadap posisi dokumen. Prakteknya seperti apa ? Mari kita bahas.
Syntax intersection observer API adalah sebagai berikut

const observer = new IntersectionObserver(obsCallback, obsOptions).

opsCallback adalah fungsi/callback yang akan dieksekusi, sedangkan obsOptions adalah parameter yang digunakan.

obsOptions memiliki beberapa parameter

obsOptions = {
root: 0,
threshold: 0.1,
rootMargin: -50px
}

root : adalah posisi dimana intersection dengan target terjadi, jika diisi dengan null artinya terhadap top-level viewport
threshold : adalah kapan akan muncul aksinya, bisa diisi dengan nilai tungal (dalam %) atau array. Jika kita isi 0.1 (sama dengan 10%) maka aksi akan muncul pada jarak 10% pada saat intersection terjadi atau 10% sebelum intersection berakhir
rootMargin : adalah batas (atas-bawah : height) dari element yang beririsan

Lengkapnya seperti ini

// Sticky navbar menggunakan intersection observer API

const obsCallback = (entries) => {
entries.forEach((entry) => {
console.log(entry);
});
};
const obsOption = {
root: null,
threshold: 0.1,
rootMargin: -50px,
};
const observer = new IntersectionObserver(obsCallback, obsOption);
observer.observe(selectors.section1);

Perhatikan posisi berikut

Pada saat Section 1 masuk viewport sebesar 0.1 viewport, observer akan mendeteksi. Property yang penting untuk diingat adalah isIntersection dan intersectionRatio, pada console nilainya adalah true (isIntersection) dan 0.10105444490909576 (intersectionRatio). Jika kita scroll terus sebelum Section 1 habis sebesar 0.1 viewport, observer akan mendeteksi perubahan tapi kali ini nilai dari isIntersection adalah false dan intersectionRatio adalah 0.09617595374584198

Logic untuk implementasi kira-kira seperti ini
Kapan kita ingin navigation bar muncul ? Pada saat header sudah tidak terlihat, ini berarti property isIntersecting _ bernilai _false

// Sticky navigation bar menggunakan Intersection Observer API

const header = document.querySelector(header);
const navHeight = selectors.navbar.getBoundingClientRect().height;
const stickyNav = (entries) => {
const [entry] = entries;

if (!entry.isIntersecting) selectors.navbar.classList.add(sticky);
else selectors.navbar.classList.remove(sticky);
};

const headerObserver = new IntersectionObserver(stickyNav, {
root: null,
threshold: 0,
rootMargin: `-${navHeight}px`,
});
headerObserver.observe(header);

Hasilnya sebagai berikut

Note : console log untu intersection observer gambarnya kurang jelas, bisa dicoba sendiri di browser masing-masig ya