Pseudo-class

Pseudo-class :hover, :focus, :nth-child, dan :not() untuk menargetkan state.

Daftar isi

Kelas dan atribut menyatakan kondisi yang tetap, sedangkan banyak keputusan gaya bergantung pada keadaan sesaat atau posisi elemen di antara saudaranya. Pseudo-class (pseudo-class) adalah kata kunci berawalan titik dua yang menambahkan kondisi tersebut pada selector tanpa mengubah markah (markup). Topik ini membahas dua kelompok pseudo-class, formula :nth-child(), pengecualian dengan :not(), serta pembedaan :focus dan :focus-visible, dan memperagakan semuanya pada demo jadwal piket.

Dua kelompok pseudo-class#

Pseudo-class keadaan (state) menjawab pertanyaan apakah elemen sedang berada dalam kondisi tertentu. Kelompok ini mencakup :hover saat penunjuk melayang di atas elemen, :focus saat elemen menerima fokus, :active saat elemen ditekan, serta :checked dan :disabled pada kontrol formulir. Pseudo-class struktural menjawab pertanyaan di posisi mana elemen berada, misalnya :first-child, :last-child, :nth-child(), dan :nth-of-type().

Kedua kelompok diperlakukan sama oleh cascade dari sisi bobot. Setiap pseudo-class sederhana menambah bobot sekelas kelas, yaitu 0,1,0, sehingga li:hover berbobot 0,1,1 dan berhak mengalahkan li yang berbobot 0,0,1. Argumen di dalam :not() dan :is() juga ikut dihitung, dan perinciannya dibahas pada bagian :not() di topik ini serta pada topik selector modern.

  • Pseudo-class keadaan bersifat dinamis dan dapat berganti kapan saja selama halaman terbuka.
  • Pseudo-class struktural dihitung ulang ketika struktur dokumen berubah, misalnya saat butir daftar ditambahkan.
  • Keduanya ditulis tanpa spasi setelah selector dasarnya, misalnya li:hover dan bukan li :hover.

Kedua kelompok juga sering digabung dalam satu selector, misalnya li:nth-child(odd):hover untuk butir ganjil yang sedang dilayangi kursor. Gabungan itu tetap berupa satu selector majemuk, sehingga bobot penyusunnya dijumlahkan sebagaimana biasa. Kebiasaan menulis pseudo-class tanpa spasi perlu dijaga karena satu spasi justru mengubah maknanya menjadi kombinator descendant.

Demo: jadwal piket dengan :hover, :focus, dan :nth-child#

Demo berikut menyajikan daftar jadwal piket dengan satu input di atasnya. Arahkan kursor ke salah satu butir untuk melihat reaksi :hover, klik input atau tekan tab hingga input menerima fokus untuk melihat :focus, dan amati latar butir yang berwarna bergantian berkat :nth-child(odd).

Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Jadwal piket</title>
  <link rel="stylesheet" href="index.css">
</head>
<body>
  <main>
    <h1>Jadwal piket kelas</h1>
    <input type="text" placeholder="Tambahkan nama pengganti">
    <ul>
      <li>Senin, Ali</li>
      <li>Selasa, Budi</li>
      <li>Rabu, Citra</li>
      <li>Kamis, Dewi</li>
      <li>Jumat, Eka</li>
      <li>Sabtu, Farhan</li>
    </ul>
  </main>

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

h1 {
  font-size: 20px;
  margin: 0 0 12px;
}

input {
  padding: 8px;
  border: 1px solid #cbd5e1;
  border-radius: 4px;
  width: 220px;
  margin-bottom: 12px;
}

ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

li {
  padding: 8px 12px;
  border-radius: 4px;
}

/* Pewarnaan bergantian dengan :nth-child */
li:nth-child(odd) {
  background-color: #e8f0fe;
}

/* Umpan balik saat butir diarahkan kursor;
   ditulis setelah aturan nth-child agar menang saat berbobot sama */
li:hover {
  background-color: #0b5394;
  color: #ffffff;
}

/* Penandaan input yang sedang menerima fokus */
input:focus {
  outline: 2px solid #0b5394;
  outline-offset: 2px;
  border-color: #0b5394;
}

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

h1 {
  font-size: 20px;
  margin: 0 0 12px;
}

input {
  padding: 8px;
  border: 1px solid #cbd5e1;
  border-radius: 4px;
  width: 220px;
  margin-bottom: 12px;
}

ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

li {
  padding: 8px 12px;
  border-radius: 4px;
}

/* Pewarnaan bergantian dengan :nth-child */
li:nth-child(odd) {
  background-color: #e8f0fe;
}

/* Umpan balik saat butir diarahkan kursor;
   ditulis setelah aturan nth-child agar menang saat berbobot sama */
li:hover {
  background-color: #0b5394;
  color: #ffffff;
}

/* Penandaan input yang sedang menerima fokus */
input:focus {
  outline: 2px solid #0b5394;
  outline-offset: 2px;
  border-color: #0b5394;
}

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

Tiga aturan pada demo itu bekerja secara independen. Aturan li:nth-child(odd) memberi latar pada butir bernomor ganjil tanpa menyentuh markah. Aturan li:hover memberi umpan balik saat kursor melayang, dan aturan input:focus memperlihatkan bidang yang sedang aktif melalui garis tepi tegas. Ketiganya memakai satu selector dasar dan satu pseudo-class, sehingga mudah dibaca dan mudah ditimpa bila diperlukan.

css Contoh: urutan aturan menentukan pemenang antar pseudo-class
/* Keduanya berbobot 0,1,1 */
li:nth-child(odd) {
  background-color: #e8f0fe;
}

/* Ditulis belakangan, sehingga menang saat kursor melayang */
li:hover {
  background-color: #0b5394;
  color: #ffffff;
}

Perhatikan juga bahwa input pada demo tidak diberi gaya hover. Perbedaan itu disengaja, karena :hover bermakna untuk elemen yang dapat diarahkan kursor sebagai target tindakan, sedangkan bidang teks dinilai melalui fokusnya. Pemisahan peran semacam ini membuat umpan balik yang ditampilkan tetap bermakna bagi pengguna.

Formula an+b pada :nth-child()#

:nth-child() menerima pola an+ban+b, dengan nn berupa bilangan cacah 0, 1, 2, dan seterusnya. Browser menomori seluruh saudara mulai dari 1, lalu mencocokkan setiap saudara yang posisinya sama dengan hasil pola tersebut. Nilai a dan b boleh bernilai negatif, dan pola tanpa b seperti 3n juga sah.

Tabel berikut memperlihatkan butir yang dicocokkan beberapa pola pada daftar dengan enam butir.

PolaButir yang dicocokkan
2nke-2, ke-4, ke-6
2n+1ke-1, ke-3, ke-5
3nke-3, ke-6
-n+3ke-1 sampai ke-3
n+4ke-4 sampai ke-6

Kata kunci odd dan even merupakan singkatan untuk 2n+1 dan 2n. Selector ini paling sering dipakai untuk pewarnaan bergantian pada tabel dan daftar, sebagaimana demo jadwal piket di atas. Nilai pola juga boleh tanpa a maupun tanpa b, sehingga :nth-child(2) yang menjangkau satu posisi tetap sah dan termasuk pola yang paling sederhana.

Pengecualian dengan :not()#

:not() mencocokkan elemen yang tidak memenuhi selector di dalam kurungnya. Argumen boleh berupa selector sederhana maupun daftar selector yang dipisah koma, sehingga li:not(:first-child, :last-child) menjangkau semua butir tengah sekaligus. Bentuk daftar ini didukung penuh oleh browser modern.

css Contoh: pengecualian dengan :not() pada butir dan input
/* Semua butir kecuali butir pertama dan terakhir mendapat garis bawah */
li:not(:first-child, :last-child) {
  border-bottom: 1px solid #cbd5e1;
}

/* Input apa pun kecuali kotak centang dan tombol pilihan */
input:not([type="checkbox"]):not([type="radio"]) {
  width: 100%;
}

Contoh kedua memakai dua :not() berantai alih-alih satu daftar, karena keduanya mencapai hasil yang sama dan pilihannya hanya masalah keterbacaan. Bentuk input:not([type="checkbox"], [type="radio"]) menjangkau jangkauan yang sama dengan satu fungsi. Apa pun bentuknya, jaga pengecualian tetap sedikit karena daftar yang panjang membuat selector sulit dinilai bobotnya secara sekilas.

Kombinasi yang tepat adalah mengecualikan kelompok kecil yang perilakunya memang berbeda, misalnya kontrol formulir yang tidak menerima teks. Untuk pengecualian yang besar, buat aturan tersendiri agar niatnya lebih jelas daripada tersembunyi di dalam fungsi. Penilaian seperti ini menjaga stylesheet tetap mudah ditelusuri saat jumlah aturan bertambah.

Dari sisi bobot, pseudo-class :not() sendiri tidak menambah apa pun, tetapi bobot argumen terkuat di dalamnya ikut dihitung. Selector li:not(.pilih) berbobot 0,2,0 karena .pilih ikut dinilai, dan hal ini perlu diperhitungkan saat aturan pengecualian bersaing dengan aturan lain. Rencanakan bobot pengecualian sejak awal agar aturan lain tidak perlu dimodifikasi ketika pengecualian baru ditambahkan.

Fokus yang ramah papan ketik#

:focus berlaku untuk semua cara pemberian fokus, baik klik maupun tab dari papan ketik, sedangkan :focus-visible hanya berlaku bila browser menilai cincin fokus perlu ditampilkan. Penilaian tersebut umumnya terpenuhi saat pengguna bernavigasi dengan papan ketik. Pola yang aman adalah membiarkan :focus-visible bekerja sesuai bawaan dan menambahkan gaya tegas, bukan menghapus garis fokus secara membabi buta. Pembedaan ini mencegah cincin fokus yang mengganggu bagi pengguna mouse tanpa menghilangkannya bagi pengguna papan ketik.

css Contoh: garis fokus pada input dan tautan melalui :focus-visible
input:focus {
  outline: 2px solid #0b5394;
  outline-offset: 2px;
}

.tautan:focus-visible {
  outline: 2px solid #0b5394;
  outline-offset: 2px;
}

Perilaku :focus-visible ditentukan browser dengan heuristik yang mengamati cara navigasi terakhir, sehingga aturan yang sama dapat menampilkan cincin pada papan ketik dan menyesuaikannya pada klik tanpa skrip apa pun. Perlakukan hasilnya sebagai umpan balik tambahan, bukan sebagai satu-satunya penanda fokus untuk kontrol yang penting. Kontrol penting sebaiknya tidak bergantung pada satu penanda saja.

:focus tetap diperlukan pada input karena pengguna perlu tahu bidang mana yang aktif, apa pun perangkatnya. Kedua pseudo-class ini dapat dipakai berdampingan tanpa saling menggugurkan, karena keduanya hanya berbeda syarat pencocokan. Penanganan :focus-visible untuk papan ketik dibahas lebih lanjut pada modul aksesibilitas, termasuk kaitannya dengan kontras cincin fokus.

Kesalahan umum#

Kesalahan pertama adalah membaca posisi pada :nth-child() sebagai posisi di antara hasil yang dicocokkan. Selector li.aktif:nth-child(2) hanya cocok bila butir kedua sekaligus membawa kelas tersebut, sehingga gagal ketika butir yang diberi kelas ternyata berada di posisi ketiga. Perbaikannya dengan menghitung ulang posisi sesungguhnya atau mengandalkan kelas.

Kesalahan kedua adalah menulis :hover sebelum aturan berbobot sama lainnya lalu menganggapnya rusak. Urutan sumber menentukan pemenang ketika bobot sama, sehingga aturan latar yang ditulis belakangan dapat menelan umpan balik hover. Tempatkan pseudo-class keadaan pada urutan akhir grup aturan yang sama.

Kesalahan ketiga adalah menganggap :not() netral terhadap bobot. Selector li:not(.pilih) tetap berbobot 0,2,0 karena argumennya dinilai, sehingga aturan pengecualian dapat menimpa aturan yang lebih sederhana tanpa disadari. Uji persaingan gaya pada DevTools sebelum menambah pengecualian baru.

Kesalahan yang menyertai adalah menguji pseudo-class hanya dengan mouse. Butir daftar yang tidak pernah dilayangi dan input yang tidak pernah diklik menyembunyikan aturan yang sebenarnya aktif pada papan ketik. Uji setiap pseudo-class keadaan dengan tab dan tombol panah agar perilakunya benar-benar terlihat.

Ringkasan#

  • Pseudo-class keadaan seperti :hover, :focus, dan :active bereaksi terhadap interaksi, sedangkan pseudo-class struktural seperti :first-child dan :nth-child() membaca posisi antarsaudara.
  • :nth-child() mencocokkan posisi yang memenuhi pola an+ban+b, dan :nth-of-type() menghitung posisi di antara saudara sejenis.
  • :not() menambah pengecualian dengan argumen sederhana maupun daftar selector, dan bobot argumen terkuatnya ikut dihitung.
  • Setiap pseudo-class sederhana berbobot sekelas kelas, sehingga urutan penulisan menentukan pemenang antar aturan berbobot sama.

Referensi#