JavaScript DOM Manipulation, Best Practices

JavaScript DOM best practices
Image: generated with AI

DOM (Document Object Model) manipulation dengan JavaScript merupakan cara untuk mengakses, memodifikasi, dan memanipulasi elemen-elemen HTML dan struktur dokumen web secara dinamis.

DOM manipulation memungkinkan kita untuk mengubah konten, atribut, gaya, atau bahkan menambahkan dan menghapus elemen pada halaman web. Juga berguna apabila kita tidak dapat mengakses langsung elemen tersebut melalui HTML-nya, maka kita dapat memanipulasinya menggunakan JavaScript.

Namun, saking saktinya, DOM manipulation perlu digunakan dengan bijaksana karena akan memengaruhi performa web. Itu sebabnya, ada beberapa best practices yang perlu diketahui oleh para web developer.


JavaScript DOM Manipulation, Best Practices

Meski tidak bersifat blocking, DOM manipulation dapat memengaruhi proses rendering halaman yang akan berpengaruh juga terhadap kecepatan loading website.

  1. Selalu deklarasikan variabel sebelum digunakan

Sebelum memanipulasi DOM, sebaiknya deklarasikan elemen yang akan kita manipulasi ke dalam variabel terlebih dahulu. Menyimpan sebuah elemen ke dalam variabel bertujuan agar elemen tersebut disimpan ke dalam memori/cache. Setelah ditemukan dan nilainya disimpan, barulah dimanipulasi.

Contoh penggunaan:

// Bad
document.getElementById("myElement").classList.add("class-name");

// Good
const element = document.getElementById("myElement"); // Deklarasikan variabel
element.classList.add("class-name"); // Manipulasi (tambahkan class)
  1. Tambahkan class alih-alih inline style

Misalkan, kita ingin membuat navbar diberi position fixed saat layar digulirkan ke bawah sehingga navbar tersebut tetap berada di atas. Banyak developer yang menggunakan pendekatan dengan langsung menambahkan inline style CSS seperti ini:

// Bad
function toggleNavStyle() {
  if (window.scrollY > siteHeader.offsetTop) {
    siteHeader.style.position = 'fixed';
    siteHeader.style.top = '0';
    siteHeader.style.width = '100%';
    siteHeader.style.zIndex = '1000';
  } else {
    siteHeader.style.position = '';
    siteHeader.style.top = '';
    siteHeader.style.width = '';
    siteHeader.style.zIndex = '';
  }
}

Outputnya:

<header id="siteHeader" style="position: fixed; top: 0; width: 100%; z-index: 1000;">
  This is the Site Header
</header>

Dalam JavaScript, ini metode yang valid, tapi tidak dengan CSS. Inline style hanya digunakan pada saat genting. Maka, alih-alih menambahkan inline style, sebaiknya kita tambahkan class agar class tersebut dapat ditangani oleh CSS.

// Good
function toggleNavClass() {
  if (window.scrollY > siteHeader.offsetTop) {
    siteHeader.classList.add('navbar-fixed');
  } else {
    siteHeader.classList.remove('navbar-fixed');
  }
}

Output:

<header id="siteHeader" class="navbar-fixed">
  This is the Site Header
</header>
  1. Event delegation

Katakanlah Anda punya satu kontainer berisi beberapa elemen button seperti contoh di bawah ini. Masing-masing button akan menjalankan aksi tertentu atau memicu suatu fungsi ketika diklik. Bagaimana cara memanipulasinya dengan JavaScript?

Insting pertama mungkin menargetkan masing-masing button atau menggunakan forEach. Sayangnya, cara itu tidak efisien, terutama bila elemennya dinamis. Ada satu cara yang lebih disarankan: event delegation.

<div class="container"> 
  <button class="button">Button 1</button>
  <button class="button">Button 2</button> 
  <button class="button">Button 3</button> 
</div>

Event delegation dalam JavaScript adalah teknik mengelola event listener dengan menargetkan elemen induk atau pembungkusnya. Ini memungkinkan kita untuk mengelola event listener dengan lebih efisien. Daripada menambahkan event listener ke setiap elemen yang mungkin ada atau akan ada, kita bisa menambahkan satu event listener ke elemen induk yang sudah ada. Event listener ini akan menangkap event dari elemen anak yang sesuai menggunakan properti event.target.

Contoh penggunaan:

// Bad
document.querySelectorAll('.button').forEach(button => {
  button.addEventListener('click', () => {
    console.log('Button clicked');
  });
});

// Good
document.querySelector('.container').addEventListener('click', (event) => {
  if (event.target.matches('.button')) {
    console.log('Button clicked');
  }
});
  1. Gunakan debouncing

Debouncing adalah teknik untuk mengatur agar suatu fungsi hanya dieksekusi setelah jeda waktu tertentu setelah terakhir kali dipanggil, menghindari pemanggilan berulang-ulang dalam waktu singkat. Ini untuk mengurangi sebuah function dipanggil berkali-kali, terutama untuk elemen yang sering digunakan seperti tombol scroll to top, resize, atau input.

function debounce(func, delay) {
  let timer; // Timer untuk mengatur debounce
  return function (...args) {
    const context = this; // Menyimpan konteks 'this'
    clearTimeout(timer); // Menghapus timer sebelumnya
    timer = setTimeout(() => func.apply(context, args), delay); // Menjadwalkan ulang
  };
}

// Contoh penggunaan: Debouncing untuk event scroll
const handleScroll = debounce(() => {
  console.log("Scroll event handled at", new Date().toISOString());
}, 300);

window.addEventListener("scroll", handleScroll);
  1. Hindari inline JavaScript

Selain menambahkan inline style seperti di poin sebelumnya, menambahkan inline JavaScript juga bukan praktik yang direkomendasikan. Untuk yang belum familiar, inline JavaScript adalah menuliskan langsung kode JavaScript di dalam elemen HTML.

//Bad
<button id="myButton" onclick="doSomething()">Click me</button>

//Good
const button = document.querySelector('#myButton');
button.addEventListener('click', doSomething);

Ada beberapa alasan mengapa inline JavaScript tidak direkomendasikan, salah satunya isu keamanan XSS (Cross-Site Scripting). Selain itu inline JavaScript sulit dikelola dan akan memengaruhi performa web.

  1. Gunakan DocumentFragment bila memungkinkan

Saat perlu menambahkan banyak elemen ke DOM, gunakan DocumentFragment untuk meningkatkan performa. DocumentFragment adalah wadah DOM sementara yang tidak terlihat di halaman, sehingga memungkinkan manipulasi elemen tanpa memengaruhi reflow atau repaint. Setelah semua elemen selesai dimanipulasi, Anda dapat menyisipkan seluruh isi DocumentFragment ke DOM dalam satu operasi, yang lebih efisien daripada menambahkan elemen satu per satu.

Contoh:

// Good
const fragment = document.createDocumentFragment();

for (let i = 0; i < 100; i++) {
  const item = document.createElement('li');
  item.textContent = `Item ${i + 1}`;
  fragment.appendChild(item);
}

document.getElementById('list').appendChild(fragment); // Satu operasi DOM

Contoh tanpa DocumentFragment:

// Bad
const list = document.getElementById('list');

for (let i = 0; i < 100; i++) {
  const item = document.createElement('li');
  item.textContent = `Item ${i + 1}`;
  list.appendChild(item); // Operasi DOM dilakukan 100 kali
}
  1. Pastikan DOM siap

DOM manipulation dijalankan di client-side atau di browser. Artinya, halaman beserta elemen yang akan dimanipulasi harus tersedia sepenuhnya di DOM terlebih dahulu. Ini sangat penting untuk elemen-elemen yang berada di bagian atas website, seperti navbar dan header.

Untuk memastikan seluruh elemen DOM sudah di-render dan siap untuk dimanipulasi, kita dapat membungkus kode JavaScript dengan event DOMContentLoaded, seperti contoh berikut.

document.addEventListener('DOMContentLoaded', () => {
    // Masukkan script DOM manipulation di sini
});

Tentu saja tidak semua DOM manipulation perlu dibungkus oleh event DOMContentLoaded.


Selain 7 poin di atas, tentu masih banyak best practices di luar sana. Dan bila Anda ingin mengoreksi atau menambahkan, dengan senang hati saya persilakan. (eL)

Langit Amaravati

Langit Amaravati

Web developer, graphic designer, techno blogger.

Suka dengan artikel-artikel di blog ini dan merasa mendapatkan manfaatnya? Dukung saya dengan mentraktir kopi. Dengan dukungan Anda, saya dapat terus menulis dan berkarya.

Hatur nuhun!

Traktir Kopi