DEV Community

Cover image for Sticky Element - JavaScript & CSS
boibolang
boibolang

Posted on • Updated on

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>

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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' });
});
Enter fullscreen mode Exit fullscreen mode

Hasilnya kurang lebih sebagai berikut

file

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

  1. Dapatkan koordinat atas (top) Section 1 terhadap koordinat window
  2. 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 1 tampil, maka harus kita dapatkan koordinat Y section 1
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');
});
Enter fullscreen mode Exit fullscreen mode

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).

file

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

file

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'
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

Perhatikan posisi berikut

file

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

file

Logic untuk implementasi kira-kira seperti ini
Kapan kita ingin navigation bar muncul ? Pada saat header sudah tidak terlihat, ini berarti property isIntersection 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);
Enter fullscreen mode Exit fullscreen mode

Hasilnya sebagai berikut

file

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

Top comments (0)