Kombinator

Selector kombinator descendant, child, dan sibling untuk memanfaatkan hubungan antar elemen.

Daftar isi

Selector tunggal hanya menyebut satu kelompok elemen tanpa memperhatikan posisinya, sedangkan kebutuhan penataan gaya sering bergantung pada hubungan antarelemen. Kombinator (combinator) adalah notasi yang menyambung dua selector atau lebih dengan memanfaatkan struktur pohon dokumen, misalnya hubungan anak langsung, keturunan, atau saudara. Topik ini membahas keempat kombinator beserta perbedaan perilakunya pada markah (markup) navigasi dan daftar, dampaknya terhadap specificity, serta panduan memilih kombinator yang tidak rapuh terhadap perubahan struktur.

Empat kombinator pada pohon dokumen#

Dokumen HTML tersusun sebagai pohon. Setiap elemen memiliki satu induk, nol atau lebih anak, nol atau lebih saudara yang berbagi induk yang sama, serta seluruh keturunannya. Kombinator memanfaatkan struktur tersebut dengan menempelkan dua selector melalui satu tanda penghubung, sehingga gaya hanya berlaku apabila kedua sisi hubungan terpenuhi. Browser tidak menambahkan elemen apa pun karena kombinator. Yang diterapkan hanyalah penyaringan target dari hubungan yang sudah ada.

Tabel berikut merangkum keempat kombinator beserta bentuk dan jangkauannya.

KombinatorBentukElemen yang dicocokkan
DescendantA BSemua B di dalam A, berapa pun kedalamannya
ChildA > BB yang merupakan anak langsung A
Adjacent siblingA + BB pertama yang tepat mengikuti A dalam satu induk
General siblingA ~ BSemua B yang mengikuti A dalam satu induk

Bagan berikut menempatkan keempat hubungan itu pada satu pohon kecil. Tautan a yang menempel langsung pada nav adalah anak langsungnya, sedangkan tautan di dalam setiap li merupakan keturunan nav yang bukan anak langsung. Antarbutir li pada satu ul berlaku hubungan saudara.

anak langsung (>)keturunan (spasi)saudara (+ dan ~)navaullilili
Relasi pohon elemen yang dijangkau keempat kombinator

Descendant dan child: perbedaan kedalaman#

Kombinator descendant ditulis sebagai satu spasi di antara dua selector. Bentuk ini mencocokkan target pada semua kedalaman, sehingga nav a tetap mengenai tautan yang terbenam di dalam beberapa lapis elemen. Kombinator child ditulis dengan tanda lebih dari dan hanya mencocokkan anak langsung. Perbedaannya terlihat jelas ketika satu navigasi memuat tautan langsung sekaligus daftar tautan bersarang.

html Contoh: nav dengan satu tautan langsung dan daftar tautan bersarang
<nav class="menu">
  <a href="#beranda">Beranda</a>
  <ul>
    <li><a href="#jadwal">Jadwal</a></li>
    <li><a href="#piket">Piket</a></li>
  </ul>
</nav>
css Contoh: descendant menjangkau semua tautan, child hanya anak langsung
/* Descendant: ketiga tautan semuanya berwarna biru */
nav a {
  color: #0b5394;
}

/* Child: hanya tautan Beranda yang mendapat latar biru */
nav > a {
  background-color: #0b5394;
  color: #ffffff;
  padding: 6px 12px;
  border-radius: 4px;
}

Pada contoh di atas, aturan nav a menghitung ketiga tautan sekaligus, sedangkan nav > a hanya menjangkau tautan Beranda karena dua tautan lain terhalang elemen ul dan li. Kombinator child dipilih apabila perbedaan satu lapis harus dipertahankan, misalnya tombol utama yang berdiri di luar daftar. Kombinator descendant dipilih untuk gaya tematik yang memang harus menembus kedalaman, misalnya warna tautan pada seluruh area navigasi.

Kedua bentuk juga dapat digabung dalam satu selector majemuk. Aturan nav > ul a menuntut ul sebagai anak langsung nav, sedangkan tautannya cukup menjadi keturunan, sehingga hubungannya tetap tepat meskipun isi butir dibungkus elemen lain. Cara membacanya adalah dari kanan ke kiri, yaitu tautan yang berada di dalam ul yang merupakan anak langsung nav.

Adjacent sibling dan general sibling#

Kedua kombinator sibling menargetkan elemen yang berbagi induk dan berada pada urutan yang lebih belakang. Kombinator + mencocokkan tepat satu saudara yang langsung mengikuti elemen pertama, sedangkan ~ mencocokkan semua saudara yang mengikuti elemen pertama tanpa harus berdekatan. Keduanya tidak melihat ke belakang, sehingga saudara yang mendahului elemen pertama tidak pernah terjangkau.

html Contoh: daftar tugas harian
<ul class="tugas">
  <li>Menyapu lantai</li>
  <li>Mencuci gelas</li>
  <li>Menyiram tanaman</li>
</ul>
css Contoh: + mencocokkan satu saudara, ~ mencocokkan semua saudara berikutnya
/* Adjacent sibling: butir apa pun yang tepat mengikuti butir lain */
li + li {
  border-top: 1px solid #cbd5e1;
}

/* Hanya paragraf pertama setelah judul yang ditebalkan */
h2 + p {
  font-weight: 600;
}

/* Semua paragraf setelah judul diwarnai abu-abu */
h2 ~ p {
  color: #52606d;
}

Pola li + li menjadi cara populer untuk menambahkan pemisah antarbutir karena tidak bergantung pada jumlah butir. Butir pertama tidak memiliki saudara yang mendahuluinya, sehingga garis atas hanya muncul mulai butir kedua. Pola semacam ini tetap benar ketika daftar bertambah atau berkurang.

Kedua kombinator sibling membaca urutan dokumen, bukan urutan tampilan. Elemen yang dipindah oleh properti seperti order tetap dihitung menurut posisi aslinya pada markah, sehingga gaya yang bergantung pada sibling tidak mengikuti susunan visual. Syarat induk yang sama juga berlaku pada keduanya, sehingga elemen yang terbungkus pembungkus berbeda tidak pernah terjangkau.

Demo berikut menerapkan keempat kombinator sekaligus pada satu halaman, lengkap dengan navigasi bersarang, pemisah antarbutir, dan kartu profil. Bandingkan setiap bagian tampilan dengan kode sumbernya untuk melihat kombinator mana yang bekerja pada bagian tersebut.

Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Demo kombinator</title>
  <link rel="stylesheet" href="index.css">
</head>
<body>
  <nav class="menu">
    <a href="#beranda">Beranda</a>
    <ul>
      <li><a href="#jadwal">Jadwal</a></li>
      <li><a href="#piket">Piket</a></li>
      <li><a href="#arsip">Arsip</a></li>
    </ul>
  </nav>

  <section class="kartu">
    <h2>Kartu profil</h2>
    <p>Nama: Citra</p>
    <p>Peran: Bendahara</p>
  </section>

<style>
/* Dasar demo */
body {
  font-family: system-ui, sans-serif;
  margin: 16px;
  color: #1f2933;
}

/* Descendant: semua tautan di dalam nav, berapa pun kedalamannya */
nav a {
  color: #0b5394;
  text-decoration: none;
}

/* Child: hanya tautan yang anak langsung nav, yaitu Beranda */
nav > a {
  background-color: #0b5394;
  color: #ffffff;
  padding: 6px 12px;
  border-radius: 4px;
}

/* Adjacent sibling: butir yang tepat mengikuti butir lain */
li + li {
  border-top: 1px solid #cbd5e1;
}

/* General sibling: semua paragraf setelah judul pada kartu */
h2 ~ p {
  color: #52606d;
  font-style: italic;
}

/* Dasar tampilan demo */
.menu ul {
  list-style: none;
  padding: 0;
  margin: 8px 0;
}
.menu li {
  padding: 6px 4px;
}
.kartu {
  border: 1px solid #cbd5e1;
  border-radius: 8px;
  padding: 4px 12px;
  margin-top: 12px;
}
.kartu h2 {
  font-size: 16px;
}

</style>
<style>
/* Dasar demo */
body {
  font-family: system-ui, sans-serif;
  margin: 16px;
  color: #1f2933;
}

/* Descendant: semua tautan di dalam nav, berapa pun kedalamannya */
nav a {
  color: #0b5394;
  text-decoration: none;
}

/* Child: hanya tautan yang anak langsung nav, yaitu Beranda */
nav > a {
  background-color: #0b5394;
  color: #ffffff;
  padding: 6px 12px;
  border-radius: 4px;
}

/* Adjacent sibling: butir yang tepat mengikuti butir lain */
li + li {
  border-top: 1px solid #cbd5e1;
}

/* General sibling: semua paragraf setelah judul pada kartu */
h2 ~ p {
  color: #52606d;
  font-style: italic;
}

/* Dasar tampilan demo */
.menu ul {
  list-style: none;
  padding: 0;
  margin: 8px 0;
}
.menu li {
  padding: 6px 4px;
}
.kartu {
  border: 1px solid #cbd5e1;
  border-radius: 8px;
  padding: 4px 12px;
  margin-top: 12px;
}
.kartu h2 {
  font-size: 16px;
}

</style></body>
</html>

Dampak kombinator pada specificity#

Kombinator sendiri tidak membawa bobot apa pun. Nilai specificity sebuah selector majemuk dihitung dengan menjumlahkan bobot setiap penyusun yang disambungkan, sehingga rantai yang panjang menumpuk bobot jenis elemen tanpa pernah mencapai bobot satu kelas. Tabel berikut memperlihatkan penjumlahan tersebut.

SelectorBobot
a0,0,1
nav a0,0,2
nav > ul li a0,0,4
.menu a0,1,1

Empat jenis elemen yang dirantai tetap kalah dari satu kelas. Kebiasaan yang sehat adalah menjangkar setiap selector majemuk pada satu kelas atau pengenal, lalu membiarkan kombinator hanya menambah ketepatan posisi. Dengan cara itu, perubahan kedalaman markah tidak serta-merta mengubah pemenang persaingan gaya.

Penjumlahan tersebut dapat diperiksa langsung pada panel Styles di DevTools, sehingga aturan yang menang dapat dipastikan tanpa ditebak. Apabila dua selector majemuk berbobot sama, urutan penulisan yang menentukan sesuai kaidah cascade. Menambah satu jenis elemen lagi di ujung rantai hanya menaikkan bobot jenis, yang hampir tidak pernah mengubah hasilnya melawan kelas.

Memilih kombinator yang tahan perubahan struktur#

Kombinator yang tepat adalah kombinator yang hubungannya memang disengaja oleh desain, bukan kebetulan urutan markah. Beberapa panduan berikut membantu memilih bentuk yang tidak mudah rusak.

  • Gunakan child ketika hubungan satu lapis merupakan bagian dari kontrak komponen, misalnya butir langsung pada daftar navigasi.
  • Gunakan descendant untuk gaya tematik yang memang harus menembus kedalaman, misalnya warna tautan pada area artikel.
  • Gunakan + untuk pola setiap elemen setelah elemen pertama, seperti pemisah antarbutir, karena pola itu tidak bergantung jumlah butir.
  • Hindari ~ untuk konten yang urutannya masih berubah, karena semua saudara setelah elemen pertama ikut terjangkau.

Panduan di atas menempatkan hubungan struktural sebagai keputusan desain, bukan jalan pintas penulisan. Selector yang singkat memang menggoda, tetapi hubungan yang tidak disengaja akan menyeret gaya ke bagian yang tidak dimaksudkan setiap kali markah berkembang. Satu kombinasi yang lazim adalah jangkar kelas pada induk terdekat disertai satu kombinator, misalnya .menu > li, yang tetap tepat meskipun isi butir ditulis ulang.

Kesalahan umum#

Kesalahan pertama adalah descendant yang dipilih padahal yang dimaksud child. Gaya untuk judul langsung pada kartu ikut menjangkau judul di dalam subbagian, sehingga tampilan tidak sesuai harapan. Perbaikannya dengan mengganti spasi menjadi > agar jangkauan berhenti pada satu lapis.

Kesalahan kedua adalah mengharapkan kombinator sibling melihat ke belakang. Kombinator + dan ~ hanya menjangkau saudara setelahnya dalam urutan dokumen, sehingga p + h2 tidak mengenai judul yang mendahului paragraf. Susun selector dari elemen yang lebih dahulu muncul, atau beri kelas pada elemen yang dimaksud.

Kesalahan ketiga adalah mengabaikan syarat induk yang sama. Kombinator h2 ~ p hanya bekerja bila kedua elemen adalah anak dari induk yang sama, sehingga paragraf di dalam div terpisah tidak pernah terjangkau. Pastikan kedua sisi benar-benar saudara sebelum menyalahkan aturan lain.

Kesalahan keempat adalah menyalin rantai panjang dari alat pemeriksa tanpa merapikannya. DevTools menawarkan selector lengkap seperti body > div > ul > li > a yang benar tetapi rapuh, karena setiap lapis pembungkus baru dapat memutuskannya. Ringkas rantai tersebut menjadi selector berjangkar kelas dengan satu kombinator sebelum menyalinnya ke stylesheet.

Ringkasan#

  • Kombinator menghubungkan dua selector berdasarkan pohon dokumen: descendant dengan spasi, child dengan >, adjacent sibling dengan +, dan general sibling dengan ~.
  • Descendant menjangkau semua kedalaman, child hanya anak langsung, + menjangkau satu saudara berikutnya, sedangkan ~ menjangkau semua saudara berikutnya.
  • Kombinator tidak membawa bobot, tetapi selector majemuknya menjumlahkan bobot semua penyusun, sehingga rantai jenis elemen tetap kalah dari satu kelas.
  • Child dan pola li + li termasuk bentuk yang paling tahan perubahan struktur karena tidak bergantung kedalaman maupun jumlah butir.

Referensi#