Zamandan bu yana (CSS ifadesiyle) farklı anlamlarda bir şelale ile çalıştık. Stiller bir "Basamaklı Stil Sayfası" oluşturur. Seçicilerimiz de kademeli olarak artıyor. Yana yatabilirler. Çoğu durumda daha aşağıya inerler. Ancak asla daha yükseğe çıkmayın. Yıllardır "Ebeveyn seçici" kurma hayali kurduk. Ve nihayet geliyor! Sözde :has()
seçicisi şeklinde.
Parametre olarak iletilen seçicilerden herhangi biri en az bir öğeyle eşleşiyorsa :has()
CSS sözde sınıfı bir öğeyi temsil eder.
Ancak bu yalnızca bir "ebeveyn"den daha fazlası seçiciyi kullanabilirsiniz. Bu, onu pazarlamanın güzel bir yoludur. Ancak "koşullu ortam", bu kadar cazip gelmeyebilir. seçiciyi kullanabilirsiniz. Ama bu, tamamen farklı. "Aile"ye ne dersin? seçici nedir?
Tarayıcı Desteği
Devam etmeden önce tarayıcı desteğinden bahsetmekte fayda var. Henüz hazır değil. Ama bu hedefe giderek yaklaşıyor. Henüz Firefox desteği yok ve şu anda yol haritasında. Ancak bu video zaten Safari'de ve Chromium 105'te yayınlanması gerekiyor. Bu makaledeki tüm demolar, kullanılan tarayıcıda desteklenip desteklenmediğini belirtir.
:has nasıl kullanılır?
Peki nasıl görünüyor? everybody
sınıfına sahip iki kardeş öğe içeren aşağıdaki HTML'yi ele alalım. a-good-time
sınıfına sahip bir alt öğesi olan sınıfı nasıl seçersiniz?
<div class="everybody">
<div>
<div class="a-good-time"></div>
</div>
</div>
<div class="everybody"></div>
:has()
kullanarak bunu aşağıdaki CSS ile yapabilirsiniz.
.everybody:has(.a-good-time) {
animation: party 21600s forwards;
}
Bu, ilk .everybody
örneğini seçer ve bir animation
uygular.
Bu örnekte, everybody
sınıfına sahip öğe hedeftir. Koşulun a-good-time
sınıfıyla bir alt öğesi var.
<target>:has(<condition>) { <styles> }
Ancak, :has()
size pek çok fırsat sunduğu için bunun çok daha ötesine de geçebilirsiniz. Hatta muhtemelen henüz keşfedilmemiş olanlar da vardır. Bunlardan birkaçını düşünün.
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şi olan label
'leri seçin. Yana gidiyoruz!
css
label:has(+ input) { … }
.
img
alt öğesinde alt
metin bulunmayan article
öğelerini seçin
css
article:has(img:not([alt])) { … }
DOM'de belirli bir durumun bulunduğu documentElement
öğesini seçin
css
:root:has(.menu-toggle[aria-pressed=”true”]) { … }
Tek sayıda alt öğeye sahip düzen kapsayıcısını seçin
css
.container:has(> .container__item:last-of-type:nth-of-type(odd)) { ... }
Izgarada fareyle üzerine gelmeyen tüm öğeleri seç
css
.grid:has(.grid__item:hover) .grid__item:not(:hover) { ... }
Özel öğe içeren kapsayıcıyı seçin <todo-list>
css
main:has(todo-list) { ... }
Doğrudan eşdüzey hr
öğesine sahip bir paragrafta yer alan her tekliyi a
seçin
css
p:has(+ hr) a:only-child { … }
Birden fazla koşulun karşılandığı bir article
seçin
css
article:has(>h1):has(>h2) { … }
Bunları birlikte kullanın. Başlığın ardından altyazının geldiği bir article
seçin
css
article:has(> h1 + h2) { … }
Etkileşimli durumlar tetiklendiğinde :root
simgesini seçin
css
:root:has(a:hover) { … }
figcaption
içermeyen bir figure
öğesinden sonra gelen paragrafı seçin
css
figure:not(:has(figcaption)) + p { … }
:has()
için ne gibi ilginç kullanım alanları var? Buradaki daha etkileyici olan şey ise sizi zihinsel modelinizi kırmaya teşvik etmesi. "Bu tarzları farklı bir şekilde ele alabilir miyim?" diye düşünmenize neden oluyor.
Örnekler
Nasıl kullanabileceğimize dair bazı örneklere göz atalım.
Kartlar
Klasik kart demosuna katılın. Kartımızda başlık, alt başlık veya medya gibi her türlü bilgiyi görüntüleyebiliriz. Temel kart bu.
<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 tanıtmak istediğinizde ne olur? Bu tasarım için kart iki sütuna bölünebilir. Daha önce, bu davranışı temsil edecek yeni bir sınıf (örneğin, card--with-media
veya card--two-columns
) oluşturuyordunuz. Bu sınıf isimlerini hatırlamak ve hatırlamak da zorlaşıyor.
:has()
ile kartta medya içeriği olduğunu tespit edip 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>
Kodu orada bırakmanız gerekmez. Bu konuda yaratıcı olabilirsiniz. "Öne çıkan" içeriği gösteren bir kart bir düzene nasıl uyum sağlayabilir? Bu CSS, öne çıkan bir kartı düzenin tam genişliğinde yapar ve ı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 çıkan bir kart dikkat çekmek için hareket ediyorsa 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 fazla olasılık var.
Formlar
Peki ya formlar? Tarzı karmaşık olmalarıyla bilinir. Buna örnek olarak girişleri ve etiketleri biçimlendirebilirsiniz. Örneğin, bir alanın geçerli olduğunu nasıl belirtiriz? :has()
ile bu iş çok daha kolay. Alakalı sözde sınıflardan (ör. :valid
ve :invalid
) yararlanabiliriz.
<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);
}
Bunu şu örnekte deneyin: Geçerli ve geçersiz değerler girmeyi ve odaklanıp odaklanmayı deneyin.
Bir alandaki hata mesajını göstermek ve gizlemek için :has()
öğesini de kullanabilirsiniz. "E-posta" alan grubumuzu alın ve bu gruba 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ı gizlenir.
.form-group__error {
display: none;
}
Ancak alan :invalid
haline geldiğinde ve alana odaklanılmadığında mesajı ekstra sınıf adlarına gerek kalmadan gösterebilirsiniz.
.form-group:has(:invalid:not(:focus)) .form-group__error {
display: block;
}
Kullanıcıların formunuzla etkileşime geçmesi için biraz ilgi çekici bir reklam ekleyemiyordunuz. Aşağıdaki örneği inceleyin. Mikro etkileşim için geçerli bir değer girdiğinizde izleyin. :invalid
değeri, form grubunun titremesine neden olur. Ancak kullanıcının hareket tercihleri yoksa devreye giriyor.
İçerik
Kod örneklerinde bu konuya değinmiştik. Peki, belge akışınızda :has()
nasıl kullanılır? Örneğin, tipografinin medyaya göre nasıl biçimlendirileceği konusunda fikir veriyor.
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 figürler bulunmaktadır. figcaption
ifadelerine sahip olmadıklarında içerik içinde gezinirler. figcaption
mevcut olduğunda tam genişliği kaplar ve daha fazla kenar boşluğu alır.
Duruma Tepki Verme
Stillerinizi işaretlememizdeki bir duruma göre reaktif hale getirmeye ne dersiniz? "Klasik" kayan gezinme çubuğu. Gezinmeyi açıp kapatan bir düğmeniz varsa aria-expanded
özelliğini kullanabilir. Uygun özellikleri güncellemek için JavaScript kullanılabilir. aria-expanded
değeri true
olduğunda, bunu algılamak ve kayan gezinme çubuğunun stillerini güncellemek için :has()
özelliğini kullanın. JavaScript kendi üzerine düşeni yapar ve CSS bu bilgilerle istediği şeyi yapabilir. İşaretlemeyi karıştırmanıza veya fazladan 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));
}
Kullanıcı hatasını önlemeye yardımcı olabilir mi?
Tüm bu örneklerin ortak noktası nedir? :has()
kullanmanın yolları gösteriliyor olsa da hiçbirinde sınıf adlarını değiştirmek gerekmiyor. Her biri yeni içerik ekledi ve bir özelliği güncelledi. Bu, kullanıcı hatalarını azaltmaya yardımcı olması açısından :has()
için büyük bir avantajdır. :has()
ile birlikte CSS, DOM'deki değişikliklere uyum sağlama sorumluluğunu üstlenebilir. JavaScript'te sınıf adlarıyla uğraşmanıza gerek olmadığından geliştirici hatası olasılığı azalır. Hepimiz bir sınıf adını yazım hatası yapıp Object
aramalarında saklamak zorunda kaldığımız durumlarda yaşadık.
Bu ilginç bir düşünce ve bizi daha net işaretlemeye ve daha az koda mı yönlendiriyor? Çok fazla JavaScript ayarlaması yapmadığımız için daha az JavaScript. Artık card card--has-media
vb. sınıflara ihtiyacınız olmadığından daha az HTML.
Kalıpların dışında düşünme
Yukarıda belirtildiği gibi :has()
, zihinsel modelinizi bozmanızı önerir. Bu, farklı şeyler denemek için bir fırsattır. Sınırları zorlamanın yollarından biri, sadece CSS ile oyun mekaniklerini geliştirmektir. Örneğin, formlar ve CSS ile adım tabanlı bir mekanik 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 size ilginç olanaklar sunuyor. Bunu, bir formu dönüşümlerle aktarmak için kullanabilirsiniz. Bu demonun ayrı bir tarayıcı sekmesinde görüntülenmesinin daha iyi olacağını unutmayın.
Eğlenmek için de klasiklere ne dersiniz? :has()
ile mekanik daha kolay oluşturulabilir. Kablo üzerine gelindiğinde oyun bitmiş demektir. Evet, bu oyun mekaniklerinden bazılarını kombinatörler (+
ve ~
) gibi öğelerle oluşturabiliriz. Ancak :has()
, ilginç işaretleme "ipuçları" kullanmak zorunda kalmadan aynı sonuçları elde etmenin bir yoludur. Bu demonun ayrı bir tarayıcı sekmesinde görüntülenmesinin daha iyi olacağını unutmayın.
Bunları yakın zamanda üretime sunmayacak olsanız da, temel öğeyi kullanabileceğiniz yöntemleri vurgularlar. Örneğin, bir :has()
zincirini bağlayabilirsiniz.
:root:has(#start:checked):has(.game__success:hover, .screen--win:hover)
.screen--win {
--display-win: 1;
}
Performans ve sınırlamalar
Son olarak, :has()
ile yapabilecekleriniz mümkün değil. :has()
ile ilgili bazı kısıtlamalar var. Başlıca sorunlar, performansa dayalı isabetlerden kaynaklanır.
:has()
:has()
ile giremezsiniz. Ancak zincire:has()
ekleyebilirsiniz.css :has(.a:has(.b)) { … }
:has()
içinde sözde öğe kullanımı yokcss :has(::after) { … } :has(::first-letter) { … }
- Yalnızca bileşik seçicileri kabul eden yapay örneklerde
:has()
kullanımını kısıtlacss ::slotted(:has(.a)) { … } :host(:has(.a)) { … } :host-context(:has(.a)) { … } ::cue(:has(.a)) { … }
- Sözde öğeden sonra
:has()
kullanımını kısıtlacss ::part(foo):has(:focus) { … }
:visited
kullanımı her zaman yanlış olurcss :has(:visited) { … }
:has()
ile ilgili gerçek performans metrikleri için bu Glitch'e göz atın. Uygulamayla ilgili bu analizleri ve ayrıntıları paylaştığı için Byungwoo'ya teşekkür ederiz.
Hepsi bu kadar!
:has()
için hazır olun. Arkadaşlarınıza bundan bahsedin ve bu gönderiyi paylaşın. Bu, CSS'ye yaklaşımımızı değiştirecektir.
Tüm demolar bu CodePen koleksiyonunda mevcuttur.