:has(), :is(), :where() ve :not(), argüman olarak bir seçici listesi alan dört fonksiyonel pseudo-class'tır. :has() bir öğeyi içerdiği ya da ardından gelen öğelere göre seçer; CSS'te uzun süre eksikliği hissedilen "ebeveyn seçici" işini görür. :is() uzun seçici listelerini kısaltır, :where() aynı işi sıfır özgüllükle yapar, :not() ise belirli seçicilerle eşleşmeyen öğeleri seçer. Dördü birlikte kullanıldığında, eskiden HTML'e sınıf eklemeyi ya da JavaScript yazmayı gerektiren pek çok stil kuralını yalnızca CSS ile yazabilirsiniz.
Bu yazıda her birinin sözdizimini, argüman kurallarını, "affedici" (forgiving) seçici listesi kavramını, özgüllüğe etkilerini ve MDN'nin özellikle uyardığı tuzakları anlatıyoruz. Özgüllük hesabının genel kuralları özgüllük hesaplama yazısında, :hover ve :nth-child() gibi tek başına çalışan pseudo-class'lar ise pseudo-class yazısında anlatılıyor.
:has(): ilişkisel seçici

:has(), kendisine argüman olarak verilen göreli seçicilerden en az biri, bu öğeye göre çapalandığında en az bir öğeyle eşleşiyorsa öğeyi temsil eder. Göreli seçici, bir birleştiriciyle başlayabilen seçicidir: > img doğrudan çocuğu, + p hemen sonraki kardeşi, ~ p sonraki kardeşlerden herhangi birini, birleştiricisiz yazılan .featured ise herhangi bir alt öğeyi ifade eder. MDN'nin anlatımıyla bu sayede bir referans öğeye göre onun ebeveynini ya da önceki kardeşini seçebilirsiniz; :has() olmadan CSS seçicileriyle bunu yapmak mümkün değildi.
/* içinde .featured olan her section */
section:has(.featured) {
border: 2px solid blue;
}
/* doğrudan çocuğu img olan kart */
.kart:has(> img) {
padding: 0;
}
/* hemen ardından h2 gelen h1: alt boşluğu küçült */
h1:has(+ h2) {
margin: 0 0 0.25rem 0;
}Son örnek MDN'nin karşılaştırmasıdır: Bir h1 başlığının ardından paragraf geliyorsa normal boşluk kalır, ardından bir h2 alt başlığı geliyorsa iki başlık birbirine yaklaştırılır. MDN bu kullanımı düzenli ifadelerdeki ileriye bakma (lookahead) yapısına benzetir: .abc:has(+ .xyz), yalnızca hemen ardından .xyz gelen .abc öğesini seçer, ama stil .xyz öğesine değil .abc öğesine uygulanır.
VE ile VEYA mantığı
:has() içinde virgülle ayrılmış birden çok seçici yazdığınızda bunlardan herhangi birinin varlığı yeterlidir: x:has(a, b), içinde a ya da b olan x öğesini seçer. Birden çok :has() zincirlediğinizde ise hepsinin var olması gerekir: x:has(a):has(b), içinde hem a hem b olan x öğesini seçer.
article:has(video, audio) {
/* video VEYA ses içeren makaleler */
}
article:has(video):has(audio) {
/* hem video HEM ses içeren makaleler */
}:has() ile :is() birlikte
:has() ve :is() birlikte kullanıldığında tek bir kural birçok başlık kombinasyonunu kapsayabilir. MDN'nin örneğinde amaç, ardından bir alt başlık gelen her başlığın alt boşluğunu küçültmektir: h1, h2 ya da h3 öğesinin hemen ardından h2, h3 ya da h4 geliyorsa boşluk daralır. Birinci :is() hangi başlıkların seçileceğini, ikinci :is() ise :has() içindeki sonraki kardeş koşulunu toplar.
:is(h1, h2, h3):has(+ :is(h2, h3, h4)) {
margin: 0 0 0.25rem 0;
}
/* aynı kuralın göreli seçici listesiyle yazımı */
:is(h1, h2, h3):has(+ h2, + h3, + h4) {
margin: 0 0 0.25rem 0;
}İki yazım aynı öğeleri seçer. İkincisinde :has() içindeki her göreli seçici kendi + birleştiricisiyle başlar ve virgüller VEYA anlamı taşır. Hangisini seçeceğiniz okunabilirlik tercihidir; uzun listelerde ilk yazım genellikle daha derli topludur. Özgüllük açısından ikisi de en özgül argümanın ağırlığını alır; burada bütün argümanlar tür seçicisi olduğu için hesap 0-0-1 artı 0-0-1, yani 0-0-2 olur.
:has() kuralları ve sınırları
:has()başka bir:has()içine yazılamaz.- Pseudo-element'ler
:has()içinde geçerli seçici değildir ve:has()için çapa da olamaz. MDN'nin açıklamasına göre birçok pseudo-element atalarının stiline bağlı olarak var olduğundan, bunların sorgulanmasına izin vermek döngüsel sorgulara yol açabilir. - Tarayıcı
:has()desteklemiyorsa,:has()affedici bir seçici listesinde (:is()ya da:where()içinde) değilse bütün seçici bloğu başarısız olur. - Özgüllüğü,
:is()ve:not()gibi, argümanlarındaki en özgül seçicinin özgüllüğüdür.
Performans: çapayı ve iç seçiciyi daraltın
MDN'nin :has() sayfası performansa ayrı bir bölüm ayırır. Tarayıcı, DOM değiştiğinde :has() koşullarını yeniden değerlendirmek zorundadır ve geniş tutulmuş seçiciler pahalı hesaplamalara yol açabilir. İki öneri öne çıkar. Birincisi, A:has(B) yazımındaki çapa A; body, :root ya da * gibi çok sayıda alt öğesi olan bir öğe olmamalıdır, çünkü o öğenin bütün alt ağacındaki her değişiklik koşulun yeniden denetlenmesini gerektirir. Bunun yerine .galeri ya da .kapsayici gibi belirli öğelere çapalayın. İkincisi, içteki B seçicisini > ya da + gibi birleştiricilerle sınırlayın: .ata:has(> .foo), .ata:has(.foo) yazımına göre çok daha dar bir alanı tarar.
/* kaçının: geniş çapa ve sınırsız iç seçici */
body:has(.kenar-cubugu) { }
.ata:has(.foo > *) { }
/* tercih edin: belirli çapa, sınırlı iç seçici */
.yerlesim:has(> .kenar-cubugu) { }
.ata:has(.foo > .belirli-cocuk) { }:is(): uzun seçici listelerini kısaltmak
:is(), bir seçici listesi alır ve bu listedeki seçicilerden herhangi biriyle seçilebilecek her öğeyi seçer. En büyük faydası tekrar eden seçici kombinasyonlarını toplamaktır. MDN'nin örneğinde, iç içe section, article, aside ve nav öğelerindeki başlıkları derinliğe göre stillemek, :is() olmadan ikinci düzeyde bile on altı ayrı seçici yazmayı gerektirir. :is() ile her düzey tek satıra iner.
/* düzey 1 */
:is(section, article, aside, nav) h1 {
font-size: 25px;
}
/* düzey 2: 4 x 4 = 16 kombinasyonun tek satırlık yazımı */
:is(section, article, aside, nav) :is(section, article, aside, nav) h1 {
font-size: 20px;
}:is() içindeki liste bir pseudo-element içeremez; bunun dışındaki basit, bileşik ve karmaşık seçicilerin hepsine izin verilir. MDN bu nedenle oge:is(::before, ::after) gibi bir yazımın işe yaramadığını, bunun yerine oge::before, oge::after yazılması gerektiğini örnekler. :is() özgüllüğe katılır: Özgüllüğü, argümanlarındaki en özgül seçicinin özgüllüğüdür.
Affedici seçici listesi: :is() ve :where() farkı

Sıradan bir seçici listesinde (virgülle ayrılmış seçicilerde) tek bir geçersiz ya da desteklenmeyen seçici bütün listeyi geçersiz kılar ve kural bloğunun tamamı yok sayılır. MDN, :is() ve :where() fonksiyonlarının belirtimde affedici seçici listesi alacak şekilde tanımlandığını belirtir. Bu listelerde her seçici ayrı ayrı ayrıştırılır; geçersiz olanlar atlanır, geçerli olanlar kullanılır.
/* desteklenmeyen seçici yüzünden kuralın TAMAMI yok sayılır */
:valid,
:desteklenmeyen {
color: green;
}
/* :desteklenmeyen atlanır, :valid yine eşleşir */
:is(:valid, :desteklenmeyen) {
color: green;
}Bu davranış, tarayıcıların henüz tanımadığı yeni bir seçiciyi mevcut bir kurala güvenle eklemenizi sağlar. Aynı nedenle :has() desteğinden emin olmadığınız bir kuralda :has() kısmını :is() içine almak, desteklemeyen tarayıcıda bütün bloğun kaybolmasını önler. :not() ise affedici değildir; bunu aşağıda göreceğiz.
:where(): sıfır özgüllüklü gruplama
:where(), :is() ile aynı şekilde çalışır, tek ve çok önemli bir farkla: Özgüllüğü her zaman sıfırdır (0-0-0). İçine ne kadar özgül seçici yazarsanız yazın, kuralın ağırlığına hiçbir şey eklemez. Bu, sonradan kolayca ezilebilmesi gereken temel stiller için idealdir: sıfırlama kuralları, bir kütüphanenin varsayılan görünümü ya da bir tasarım sisteminin taban stilleri.
MDN'nin karşılaştırma örneğini Türkçeleştirerek inceleyelim. İki ayrı grup bağlantı rengi tanımlanıyor, sonra alt bilgideki bağlantılar yalnızca tür seçicilerinden oluşan bir kuralla mavi yapılmak isteniyor.
:is(section.is-stil, aside.is-stil, footer.is-stil) a {
color: red;
/* özgüllük: en özgül argüman footer.is-stil = 0-1-1, artı a = 0-0-1, toplam 0-1-2 */
}
:where(section.where-stil, aside.where-stil, footer.where-stil) a {
color: orange;
/* özgüllük: :where() 0-0-0, artı a = 0-0-1, toplam 0-0-1 */
}
footer a {
color: blue;
/* özgüllük: 0-0-2 */
}Sonucu hesaplayalım. footer a kuralının özgüllüğü iki tür seçicisinden gelir: 0-0-2. :is() kuralının özgüllüğü 0-1-2'dir; sınıf sütunundaki 1, tür sütunundaki 2'den önce karşılaştırıldığı için :is() kuralı kazanır ve is-stil alt bilgisindeki bağlantı kırmızı kalır. :where() kuralının özgüllüğü ise yalnızca 0-0-1'dir; 0-0-2 daha büyük olduğu için footer a kazanır ve where-stil alt bilgisindeki bağlantı maviye döner. MDN'nin örneğindeki sonuç da budur.
:not(): olumsuzlama

:not(), verilen seçici listesiyle eşleşmeyen öğeleri seçer; bu yüzden olumsuzlama pseudo-class'ı olarak bilinir. Liste pseudo-element içeremez. MDN bu pseudo-class için ayrı bir "tuhaflıklar ve beklenmedik sonuçlar" listesi verir; bunları bilmek :not() ile yazılan kuralların neden beklenenden geniş ya da dar çalıştığını anlamanızı sağlar.
- Özgüllüğü artırabilir:
#foo:not(#bar),#fooile aynı öğeyi seçer ama iki id seçicisinin özgüllüğüne sahiptir. Genel kural olarak:not()özgüllüğü, argümanındaki en özgül seçicinin özgüllüğüyle değiştirilir. htmlvebodydahil her şeyi seçer::not(.foo),.fooolmayan her öğeyle eşleşir.- Alt birleştiricilerle beklenmedik eşleşir:
body :not(table) a, tablo içindeki bağlantılara da uygulanır, çünkütr,tdgibi ara öğeler:not(table)kısmıyla eşleşebilir. Tablo içinde olmayan bağlantıları seçmek içinbody a:not(table a)yazın. - Liste kısaltması:
:not(.foo, .bar),:not(.foo):not(.bar)ile eşdeğerdir. - Affedici değildir: Argümandaki seçicilerden biri geçersiz ya da desteklenmiyorsa bütün kural geçersiz olur. Çözüm, listeyi
:is()içine almaktır::not(:is(.foo, :gecersiz-sozde-sinif)). - Anlamsız seçiciler yazılabilir:
:not(*)"öğe olmayan öğe" demektir ve hiçbir zaman eşleşmez.
/* son öğe hariç tüm liste öğelerine alt çizgi */
.liste li:not(:last-child) {
border-bottom: 1px solid #dddddd;
}
/* tablo içinde olmayan bağlantılar */
body a:not(table a) {
text-decoration: underline;
}
/* geçersiz seçiciye dayanıklı olumsuzlama */
p:not(:is(.foo, :gecersiz-sozde-sinif)) {
color: green;
}Özgüllük özeti: adım adım hesaplar
MDN'nin özgüllük rehberine göre :is(), :has() ve :not() pseudo-class'larının kendisi özgüllüğe ağırlık eklemez; ağırlık, parantez içindeki listede en yüksek özgüllüğe sahip argümandan gelir. :where() ise argümanları ne olursa olsun her zaman 0-0-0 değerindedir. Aşağıdaki tabloda id-sınıf-tür sütunlarıyla hesaplar yer alıyor.
| Seçici | Hesap | Özgüllük |
|---|---|---|
| :is(#ana, .kart) p | en özgül argüman #ana 1-0-0, artı p 0-0-1 | 1-0-1 |
| :where(#ana, .kart) p | :where() 0-0-0, artı p 0-0-1 | 0-0-1 |
| p:not(.fancy) | p 0-0-1, artı .fancy 0-1-0 | 0-1-1 |
| section:has(.featured) | section 0-0-1, artı .featured 0-1-0 | 0-1-1 |
| h1:has(+ h2, > #x) | h1 0-0-1, artı en özgül argüman #x 1-0-0 | 1-0-1 |
| #foo:not(#bar) | #foo 1-0-0, artı #bar 1-0-0 | 2-0-0 |
İlk satıra dikkat edin: :is(#ana, .kart) p seçicisi bir .kart içindeki paragrafla eşleştiğinde bile özgüllüğü 1-0-1'dir, çünkü özgüllük eşleşen argümandan değil, listedeki en özgül argümandan hesaplanır. Bu yüzden :is() içine bir id koymak, o listedeki bütün eşleşmeleri ezmesi zor hâle getirir. Böyle bir durumda :where() kullanmak ya da id'yi listeden çıkarmak daha güvenlidir.
Uygulamalı mini örnek: form alanı ve kart düzeni
Şimdi dört pseudo-class'ı gerçek bir senaryoda birleştirelim. Kütüphane benzeri taban stiller :where() ile sıfır özgüllükte tanımlanıyor; görselli kartlar :has() ile farklı bir düzen alıyor; geçersiz alan içeren form grubu etiketiyle birlikte vurgulanıyor; son kart hariç kartlara alt boşluk veriliyor.
<div class="alan">
<label for="eposta">E-posta</label>
<input id="eposta" type="email" required />
</div>
<article class="kart">
<img src="kapak.jpg" alt="Kapak görseli" />
<h3>Başlık</h3>
</article>/* taban stiller: sonradan her kuralla kolayca ezilir */
:where(.kart, .alan) {
border-radius: 8px;
padding: 1rem;
}
/* görsel içeren kart: dolgu yok, görsel kenara yaslanır */
.kart:has(> img) {
padding: 0;
}
/* kullanıcı etkileşiminden sonra geçersiz alan içeren grup */
.alan:has(input:user-invalid) label {
color: crimson;
}
/* son kart hariç alt boşluk */
.kart:not(:last-child) {
margin-bottom: 1.5rem;
}Burada .kart:has(> img) kuralının özgüllüğü 0-1-1, taban :where() kuralınınki ise 0-0-0'dır; bu yüzden dolgunun sıfırlanması herhangi bir sıra ya da !important hilesi gerektirmez. .alan:has(input:user-invalid) label kuralı ise çocuğun durumuna göre kardeşini (etiketi) stiller; bu, yalnızca birleştiricilerle yapılamazdı, çünkü etiket alandan önce geliyor. :user-invalid ayrıntıları pseudo-class yazısında.
Sık yapılan hatalar
body:has()ile her şeyi bağlamak: Geniş çapalar DOM her değiştiğinde pahalı yeniden değerlendirmelere yol açabilir; çapayı daraltın.:has()içinde pseudo-element aramak:.kutu:has(::before)geçerli değildir.:not()listesine desteklenmeyen seçici koymak: Bütün kural kaybolur; listeyi:is()ile sarın.body :not(table) aile tablodaki bağlantıları dışlamaya çalışmak: Ara öğeler yüzünden yine eşleşir;a:not(table a)yazın.:is()içine id koyup sonra ezememek: Özgüllük listedeki en özgül argümandan gelir; taban stiller için:where()kullanın.:is()ile pseudo-element seçmek::is()pseudo-element'lerle eşleşmez.
Sık Sorulan Sorular
CSS'te ebeveyn seçici var mı?
Evet, :has() bu işi görür. ebeveyn:has(> cocuk) doğrudan çocuğa, ebeveyn:has(cocuk) herhangi bir alt öğeye göre ebeveyni seçer. Aynı yöntemle onceki:has(+ sonraki) yazarak önceki kardeşi de seçebilirsiniz.
:is() ile :where() arasında nasıl seçim yaparım?
İkisi aynı öğeleri seçer. Kuralın normal ağırlıkla uygulanmasını istiyorsanız :is(), sonradan kolayca ezilebilmesini istiyorsanız (sıfırlamalar, varsayılanlar, kütüphane stilleri) :where() kullanın. :where() özgüllüğü her zaman 0-0-0'dır.
:not() içine birden çok seçici yazabilir miyim?
Evet, virgülle: :not(.foo, .bar), ne .foo ne .bar olan öğeleri seçer ve :not(.foo):not(.bar) ile eşdeğerdir. Listedeki herhangi bir seçici geçersizse bütün kuralın yok sayılacağını unutmayın.
:where() içine id yazmanın bir anlamı var mı?
Evet. MDN'nin özgüllük rehberine göre bir seçiciyi hedef açısından daha belirgin hâle getirmek ama özgüllüğünü hiç artırmamak istediğinizde id ya da seçicinin herhangi bir parçasını :where() içine yazabilirsiniz. Örneğin :where(#icerik) a, yalnızca #icerik içindeki bağlantıları seçer ama özgüllüğü sıradan a seçicisi kadardır (0-0-1). Böylece kural doğru öğeleri hedefler, yine de sonradan yazılan basit bir kuralla ezilebilir.
:has() performansı kötü mü etkiler?
Her kullanım değil. MDN'ye göre sorun, çok sayıda alt öğesi olan bir öğeye çapalanmış ya da içteki seçicisi sınırlanmamış kurallarda ortaya çıkar. Çapayı belirli bir kapsayıcıyla, iç seçiciyi > ya da + gibi birleştiricilerle sınırlamak yeniden değerlendirme maliyetini düşürür.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: CSS öğrenme rehberi · CSS özgüllük hesaplama · CSS pseudo-class: :hover, :focus, :nth-child() · CSS birleştiriciler · CSS @supports özellik sorguları · CSS nesting: iç içe yazım · CSS @layer cascade katmanları