CSS terimleriyle ifade edecek olursak, zaman başlangıcından beri çeşitli şekillerde basamaklı bir sistemle çalıştık. Stillerimiz bir "Geçiş Stil Sayfası"ndan oluşur. Seçicilerimiz de basamaklıdır. Yan yana gidebilirler. Çoğu durumda bu değerler aşağı doğru hareket eder. Ancak asla yukarı doğru değil. Yıllardır "ebeveyn seçici" hakkında hayal kuruyorduk. Ve sonunda bu özellik kullanıma sunuluyor. :has()
sözde seçici şeklinde.
Parametre olarak iletilen seçicilerden herhangi biri en az bir öğeyle eşleşirse :has()
CSS sözde sınıfı bir öğeyi temsil eder.
Ancak bu, "ebeveyn" seçicisinden daha fazlasıdır. Bu, ürününüzü pazarlamak için iyi bir yöntem. "Koşullu ortam" seçici, pek cazip olmayan bir yöntemdir. Ancak bu ifade kulağa pek hoş gelmiyor. "aile" seçicisini denediniz mi?
Tarayıcı desteği
Devam etmeden önce tarayıcı desteğinden bahsetmek isteriz. Henüz o seviyede değiliz. Ancak bu duruma yaklaşıyoruz. Henüz Firefox desteği yok ancak bu özellik gelecekteki planlarımız arasında yer alıyor. Ancak bu özellik zaten Safari'de mevcut ve Chromium 105'te kullanıma sunulacak. Bu makaledeki tüm demolar, kullanılan tarayıcıda desteklenip desteklenmediğini belirtir.
:has işlevini kullanma
Nasıl görünüyor? everybody
sınıfına sahip iki kardeş öğenin bulunduğu aşağıdaki HTML'yi düşünün. a-good-time
sınıfına sahip bir alt öğesi olan öğeyi nasıl seçersiniz?
<div class="everybody">
<div>
<div class="a-good-time"></div>
</div>
</div>
<div class="everybody"></div>
:has()
ile bunu aşağıdaki CSS ile yapabilirsiniz.
.everybody:has(.a-good-time) {
animation: party 21600s forwards;
}
Bu işlem, .everybody
öğesinin ilk örneğini seçer ve bir animation
uygular.
Bu örnekte, everybody
sınıfına sahip öğe hedeftir. Koşul, a-good-time
sınıfına sahip bir alt öğenin olmasıdır.
<target>:has(<condition>) { <styles> }
Ancak :has()
birçok fırsat sunduğundan bu süreci çok daha ileri götürebilirsiniz. Hatta henüz keşfedilmemiş olanlar için bile. Bunlardan bazılarını göz önünde bulundurun.
Doğrudan figcaption
içeren figure
öğelerini seçin.
css
figure:has(> figcaption) { ... }
Doğrudan SVG alt öğesi olmayan anchor
'leri seçin
css
a:not(:has(> svg)) { ... }
Doğrudan input
kardeş öğesi olan label
'leri seçin. Yan tarafa doğru gidiyoruz.
css
label:has(+ input) { … }
img
alt öğesinin alt
metnine sahip olmadığı article
öğelerini seçin
css
article:has(img:not([alt])) { … }
DOM'da bir durumun bulunduğu documentElement
öğesini seçin
css
:root:has(.menu-toggle[aria-pressed=”true”]) { … }
Çocuk sayısı tek olan düzen kapsayıcısını seçin
css
.container:has(> .container__item:last-of-type:nth-of-type(odd)) { ... }
Gri üzerine gelmeyen tüm öğeleri seçin
css
.grid:has(.grid__item:hover) .grid__item:not(:hover) { ... }
<todo-list>
özel öğesini içeren kapsayıcıyı seçin
css
main:has(todo-list) { ... }
Doğrudan kardeş hr
öğesi olan bir paragraftaki her tek a
öğesini seçin
css
p:has(+ hr) a:only-child { … }
Birden fazla koşulun karşılandığı bir article
öğesini seçin
css
article:has(>h1):has(>h2) { … }
Bunları karıştırın. Başlığın ardından altyazı gelen bir article
seçin
css
article:has(> h1 + h2) { … }
Etkileşimli durumlar tetiklendiğinde :root
'yi seçin
css
:root:has(a:hover) { … }
figcaption
içermeyen bir figure
'den sonra gelen paragrafı seçin
css
figure:not(:has(figcaption)) + p { … }
:has()
için ilginç kullanım alanları nedir? Buradaki en ilgi çekici şey, zihinsel modelinizi yıkmaya teşvik etmenizdir. "Bu stillere farklı bir şekilde yaklaşabilir miyim?" diye düşünmenizi sağlar.
Örnekler
Bu özelliği nasıl kullanabileceğimize dair bazı örneklere göz atalım.
Kartlar
Klasik kart demosunu kullanın. Kartımızda herhangi bir bilgiyi (ör. başlık, altyazı veya medya) gösterebiliriz. Temel kart aşağıda verilmiştir.
<li class="card">
<h2 class="card__title">
<a href="#">Some Awesome Article</a>
</h2>
<p class="card__blurb">Here's a description for this awesome article.</p>
<small class="card__author">Chrome DevRel</small>
</li>
Medya eklemek istediğinizde ne olur? Bu tasarımda kart iki sütuna bölünebilir. Öncelikle bu davranışı temsil edecek yeni bir sınıf (ör. card--with-media
veya card--two-columns
) oluşturabilirsiniz. Bu sınıf adlarının hem oluşturulması hem de bakımı ve hatırlanması zorlaşır.
:has()
ile kartta medya olduğunu algılayabilir ve uygun işlemi yapabilirsiniz. Değiştirici sınıf adlarına gerek yoktur.
<li class="card">
<h2 class="card__title">
<a href="/article.html">Some Awesome Article</a>
</h2>
<p class="card__blurb">Here's a description for this awesome article.</p>
<small class="card__author">Chrome DevRel</small>
<img
class="card__media"
alt=""
width="400"
height="400"
src="./team-awesome.png"
/>
</li>
Ayrıca, bu durumda bırakmanız gerekmez. Yaratıcı sorgulardan da faydalanabilirsiniz. "Öne çıkan" içeriği gösteren bir kart, bir düzene nasıl uyarlanabilir? Bu CSS, öne çıkarılan kartı düzenin tam genişliğine getirir ve bir ızgaranın başına yerleştirir.
.card:has(.card__banner) {
grid-row: 1;
grid-column: 1 / -1;
max-inline-size: 100%;
grid-template-columns: 1fr 1fr;
border-left-width: var(--size-4);
}
Banner'ı olan öne çıkarılan bir kart dikkat çekmek için sallanıyorsa ne olur?
<li class="card">
<h2 class="card__title">
<a href="#">Some Awesome Article</a>
</h2>
<p class="card__blurb">Here's a description for this awesome article.</p>
<small class="card__author">Chrome DevRel</small>
<img
class="card__media"
alt=""
width="400"
height="400"
src="./team-awesome.png"
/>
<div class="card__banner"></div>
</li>
.card:has(.card__banner) {
--color: var(--green-3-hsl);
animation: wiggle 6s infinite;
}
Çok sayıda olasılık var.
Formlar
Formlar ne olacak? Bu saçların şekillendirmesinin zor olduğu bilinir. Girişlere ve etiketlerine stil uygulamak bu tür bir örnektir. Örneğin, bir alanın geçerli olduğunu nasıl belirtiriz? :has()
ile bu işlem çok daha kolay hale gelir. İlgili form sözde sınıflarına (ör. :valid
ve :invalid
) bağlanabiliriz.
<div class="form-group">
<label for="email" class="form-label">Email</label>
<input
required
type="email"
id="email"
class="form-input"
title="Enter valid email address"
placeholder="Enter valid email address"
/>
</div>
label {
color: var(--color);
}
input {
border: 4px solid var(--color);
}
.form-group:has(:invalid) {
--color: var(--invalid);
}
.form-group:has(:focus) {
--color: var(--focus);
}
.form-group:has(:valid) {
--color: var(--valid);
}
.form-group:has(:placeholder-shown) {
--color: var(--blur);
}
Bu örnekte deneyebilirsiniz: Geçerli ve geçersiz değerler girip odağı açıp kapatmayı deneyin.
Bir alanın hata mesajını göstermek ve gizlemek için :has()
simgesini de kullanabilirsiniz. "e-posta" alan grubumuzu alıp buna bir hata mesajı ekleyin.
<div class="form-group">
<label for="email" class="form-label">
Email
</label>
<div class="form-group__input">
<input
required
type="email"
id="email"
class="form-input"
title="Enter valid email address"
placeholder="Enter valid email address"
/>
<div class="form-group__error">Enter a valid email address</div>
</div>
</div>
Varsayılan olarak hata mesajını gizlersiniz.
.form-group__error {
display: none;
}
Ancak alan :invalid
olduğunda ve odaklanmadığında, ek sınıf adlarına gerek kalmadan mesajı gösterebilirsiniz.
.form-group:has(:invalid:not(:focus)) .form-group__error {
display: block;
}
Kullanıcılarınızın formunuzla etkileşime geçtiğinde hoş bir dokunuş ekleyebilirsiniz. Aşağıdaki örneği inceleyin. Mikro etkileşim için geçerli bir değer girdiğinizde ne olduğunu izleyin. :invalid
değeri, form grubunun titremesine neden olur. Ancak bu işlem yalnızca kullanıcının hareket tercihi yoksa yapılabilir.
İçerik
Bu konuya kod örneklerimizde değinmiştik. Ancak :has()
'ü doküman akışınızda nasıl kullanabilirsiniz? Örneğin, medya çevresindeki yazı tiplerini nasıl biçimlendirebileceğimizle ilgili fikirler sunar.
figure:not(:has(figcaption)) {
float: left;
margin: var(--size-fluid-2) var(--size-fluid-2) var(--size-fluid-2) 0;
}
figure:has(figcaption) {
width: 100%;
margin: var(--size-fluid-4) 0;
}
figure:has(figcaption) img {
width: 100%;
}
Bu örnekte şekiller bulunmaktadır. figcaption
olmadığında içerik içinde yüzer. figcaption
mevcut olduğunda tam genişliği kaplar ve ekstra kenar boşluğu alır.
Duruma tepki verme
Stillerinizi, işaretlememizdeki bazı durumlara duyarlı hale getirmeye ne dersiniz? "Klasik" kaydırmalı gezinme çubuğu içeren bir örnek düşünün. Gezinme menüsünü açan/kapayan bir düğmeniz varsa bu düğmede aria-expanded
özelliği kullanılabilir. Uygun özellikleri güncellemek için JavaScript kullanılabilir. aria-expanded
true
olduğunda bunu algılamak ve kaydırmalı gezinme menüsünün stillerini güncellemek için :has()
değerini kullanın. JavaScript kendi görevini yapar ve CSS bu bilgilerle istediğini yapabilir. İşaretleri karıştırmanıza veya ek sınıf adları eklemenize gerek yoktur. (Not: Bu, üretime hazır bir örnek değildir.)
:root:has([aria-expanded="true"]) {
--open: 1;
}
body {
transform: translateX(calc(var(--open, 0) * -200px));
}
:has, kullanıcı hatalarını önlemeye yardımcı olabilir mi?
Bu örneklerin ortak noktası nedir? :has()
'ü kullanmanın yollarını göstermenin yanı sıra bu yöntemlerden hiçbiri sınıf adlarının değiştirilmesini gerektirmedi. Her biri yeni içerik ekledi ve bir özelliği güncelledi. Bu, kullanıcı hatalarını azaltmaya yardımcı olabileceğinden :has()
'ün en büyük avantajlarından biridir. :has()
sayesinde CSS, DOM'daki değişikliklere uyum sağlama sorumluluğunu üstlenebilir. JavaScript'te sınıf adlarıyla uğraşmanız gerekmez. Bu sayede geliştirici hatası olasılığı azalır. Sınıf adında yazım hatası yaptığımız ve bunları Object
aramalarında tutmaya başvurduğumuz durumlar hepimizin başına gelmiştir.
İlginç bir düşünce. Bu, daha temiz işaretleme ve daha az kod kullanmamıza yol açar mı? Daha az JavaScript ayarlaması yaptığımız için daha az JavaScript. Artık card card--has-media
gibi sınıflara ihtiyacınız olmadığından daha az HTML.
Kalıpların dışına çıkarak düşünme
Yukarıda da belirtildiği gibi, :has()
zihinsel modeli kırmanızı teşvik eder. Farklı şeyler denemek için bir fırsattır. Sınırları zorlamanın bir yolu da oyun mekaniklerini yalnızca CSS ile oluşturmaktır. Örneğin, formlar ve CSS ile adıma dayalı bir mekanizma oluşturabilirsiniz.
<div class="step">
<label for="step--1">1</label>
<input id="step--1" type="checkbox" />
</div>
<div class="step">
<label for="step--2">2</label>
<input id="step--2" type="checkbox" />
</div>
.step:has(:checked), .step:first-of-type:has(:checked) {
--hue: 10;
opacity: 0.2;
}
.step:has(:checked) + .step:not(.step:has(:checked)) {
--hue: 210;
opacity: 1;
}
Bu da ilginç olasılıklar sunuyor. Dönüşüm içeren bir formu incelemek için bunu kullanabilirsiniz. Bu demoyu ayrı bir tarayıcı sekmesinde görüntülemeniz önerilir.
Eğlenmek için klasik Buzz Wire oyununa ne dersiniz? :has()
ile bu mekanizmayı oluşturmak daha kolaydır. Fare imleci kablonun üzerine gelirse oyun biter. Evet, bu oyun mekaniklerinden bazılarını kardeş kombinatörler (+
ve ~
) gibi öğelerle oluşturabiliriz. Ancak :has()
, ilginç işaretleme "hileleri" kullanmak zorunda kalmadan aynı sonuçlara ulaşmanın bir yoludur. Bu demoyu ayrı bir tarayıcı sekmesinde görüntülemeniz önerilir.
Bu örnekleri yakın zamanda üretime dahil etmeyecek olsanız da bu örnekler, ilkelleri kullanabileceğiniz yolları vurgular. Örneğin, :has()
öğesini zincirleme
:root:has(#start:checked):has(.game__success:hover, .screen--win:hover)
.screen--win {
--display-win: 1;
}
Performans ve sınırlamalar
Sohbeti sonlandırmadan önce, :has()
ile ne yapamadığınızı öğrenebilir miyim? :has()
ile ilgili bazı kısıtlamalar vardır. Bunlardan en önemlisi, performanstaki düşüşlerden kaynaklanır.
:has()
'yi:has()
yapamazsınız. Ancak:has()
kullanabilirsiniz.css :has(.a:has(.b)) { … }
:has()
css :has(::after) { … } :has(::first-letter) { … }
içinde sözde öğe kullanımı yok- Yalnızca bileşik seçicileri kabul eden sözde seçicilerde
:has()
kullanımının kısıtlanmasıcss ::slotted(:has(.a)) { … } :host(:has(.a)) { … } :host-context(:has(.a)) { … } ::cue(:has(.a)) { … }
- Sözde öğe
css ::part(foo):has(:focus) { … }
'den sonra:has()
kullanımını kısıtlama :visited
kullanımı her zaman yanlış olacaktırcss :has(:visited) { … }
:has()
ile ilgili gerçek performans metrikleri için bu Glitch sayfasına göz atın. Bu analizleri ve uygulamayla ilgili ayrıntıları paylaşan Byungwoo'ya teşekkür ederiz.
Hepsi bu kadar!
:has()
için hazırlanın. Bu özelliği arkadaşlarınızla paylaşın ve bu yayını dağıtın. CSS'ye yaklaşımımızı kökten değiştirecek bir özellik.
Tüm demolar bu CodePen koleksiyonunda mevcuttur.