Selector CSS mengalir dari atas ke bawah: ul li memilih li di dalam ul, sedangkan kebalikannya — memilih ul karena isinya — lama tidak dimungkinkan tanpa JavaScript. Pseudo-class :has()
menutup celah tersebut: elemen kini dapat dipilih berdasarkan keturunan atau saudaranya, misalnya kartu yang memuat gambar mendapat tata letak berbeda, atau form yang berisi isian bermasalah mendapat garis peringatan. Fitur ini merupakan Baseline sejak 2023 dan dapat dipakai di seluruh browser utama.
Menulis Selector Relasional #
.kartu:has(img) cocok dengan elemen .kartu yang memuat sekurang-kurangnya satu img. Argumen di dalam :has() dibaca relatif terhadap elemen yang diuji: img berarti “keturunan bernama img”. Pola ini menerima selector lengkap — :has(img.cover) menuntut class tertentu — dan combinator saudara pun bekerja: .label:has(+ input:invalid) menunjuk label yang diikuti langsung oleh input invalid. Contoh sehari-hari lainnya: figure:has(figcaption) memberi jarak khusus untuk gambar berketerangan, sedangkan nav:has(a[aria-current]) menandai navigasi yang memuat tautan halaman aktif. Berikut gaya kartu dua wajah:
.kartu {
padding: 16px;
border: 1px solid rgb(226, 232, 240);
border-radius: 12px;
background-color: rgb(241, 245, 249);
}
.kartu:has(img) {
display: flex;
align-items: center;
gap: 12px;
background-color: rgb(255, 255, 255);
}- Baris 1–7 (
.kartu): keadaan dasar kartu — latar abu kebiruan pucat, tampil sebagai blok biasa. - Baris 9–14 (
:has(img)): kartu yang memuat gambar berubah menjadi baris flex — gambar dan teks sejajar dengan jarak 12px — dan latarnya putih. Satu markup, dua tata letak, tanpa class tambahan.
Hasil di Browser:
Kartu pertama yang memuat gambar tampil dengan display flex dan latar
rgb(255, 255, 255). Kartu kedua tanpa gambar tetap display block dengan
latar rgb(241, 245, 249). Label pilihan tercentang berwarna rgb(22, 101, 52)
dan tebal; label netral berwarna rgb(51, 65, 85). Kartu form yang berisi
isian wajib kosong berborder rgb(220, 38, 38).Kondisi pada Label dan Form #
:has() paling terasa manfaatnya ketika keadaan anak menentukan tampilan induk. Dua pola yang paling sering dipakai:
.pilihan {
display: flex;
align-items: center;
gap: 8px;
color: rgb(51, 65, 85);
}
.pilihan:has(input:checked) {
color: rgb(22, 101, 52);
font-weight: 600;
}
.form-kirim {
padding: 16px;
border: 1px solid rgb(226, 232, 240);
border-radius: 12px;
}
.form-kirim:has(input:invalid) {
border-color: rgb(220, 38, 38);
}.pilihan:has(input:checked): seluruh label menyala ketika kotak centang di dalamnya dipilih — induk mengikuti keadaan anaknya, kebalikan dariinput:checked + spanyang hanya menata saudara setelahnya..form-kirim:has(input:invalid): kartu form mendapat border merah selama ada isian yang gagal validasi — fondasi yang pas untuk panel ringkasan galat pada halaman panjang.
Dua catatan teknis melengkapi pola di atas. Pertama, :has() bersifat hidup: selector dievaluasi ulang setiap kali DOM berubah — checkbox dipilih, gambar ditambahkan, isian menjadi invalid — dan tampilan mengikuti tanpa skrip. Kedua, spesifikasi melarang :has() disarangkan di dalam argumen :has() lain; pola seperti .kartu:has(.tandai:has(img)) tidak valid, meskipun menggandakan :has() pada satu selector — .kartu:has(img):has(span) — tetap sah. Untuk membatasi kedalaman pencarian, gunakan combinator anak: .kartu:has(> img) hanya melihat img yang menjadi anak langsung, sehingga pencocokan lebih terarah.
Fallback dan Specificity #
Karena :has() relatif baru, lindungi halaman dengan pendekatan progressive enhancement: tulis gaya dasar yang berfungsi di mana saja, lalu naikkan penyempurnaan di dalam @supports. Deteksi @supports selector(:has(img)) berjalan tanpa memuat sumber daya tambahan, sehingga aman dipasang pada stylesheet produksi:
@supports selector(:has(img)) {
.kartu:has(img) {
display: flex;
gap: 12px;
}
}Satu hal lagi yang perlu dipegang: specificity :has() mengikuti argumen paling spesifik di dalamnya. Sebagai gambaran, .kartu:has(img.cover) dihitung dua class dan satu type — berasal dari .kartu ditambah argumen terkuatnya img.cover, bukan dari teks selector di luarnya. .kartu:has(#unggulan) bernilai seperti selector ber-id — kuat menimpa aturan lain, dan sebaiknya dihindari bila tidak disengaja; pilih argumen secukupnya.
Uji manual di demo:
- Buka demo — kartu bergambar tampil sebagai baris flex berlatar putih sejak dimuat, karena
imgada di dalamnya; kartu tanpa gambar tetap blok abu. - Klik checkbox — label menyala hijau dan tebal; klik lagi untuk memadamkannya.
- Kosongkan isian wajib pada kartu form — border merah aktif; isi kembali — border kembali normal.
Demo chapter ini (kartu-has.html) membawa lima penanda data-expected pada properti display, background-color, color, font-weight, dan border-color.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
contoh-kartu-has.css
Unduh berkas.kartu {
padding: 16px;
border: 1px solid rgb(226, 232, 240);
border-radius: 12px;
background-color: rgb(241, 245, 249);
}
.kartu:has(img) {
display: flex;
align-items: center;
gap: 12px;
background-color: rgb(255, 255, 255);
}