Metin2Games Discord sunucusuna katılmak ve anlık destek almak için tıkla!
1

JavaScript'siz Koşullu Stil: CSS:has ile Ebeveyni, Önceki Kardeşi ve Hatalı Form Alanını Seçen Seçiciler Yazmak

Kayıtlı
1 Yıl 9 Ay 26 Gün
Katılım
10 Ocak 2025
Konular
163
Mesajlar
257
Tepkime puanı
36
Online süresi
11h 19m
Puan
53
Konum
Türkiye
Web sitesi
www.zyncra.shop
JavaScript'siz Koşullu Stil: CSS:has ile Ebeveyni, Önceki Kardeşi ve Hatalı Form Alanını Seçen Seçiciler Yazmak

📂 Kategori: cPanel / Web • ⏱ Okuma Süresi: ~7 dk • 💻 Kod: CSS

konu-e2ea7a1a5d.jpg

1. Çocuğuna bakarak ebeveynini boyayan CSS seçicisi :has() ile kart, form, menü ve liste düzenlerini JavaScript'e el atmadan yönetin

İçinde alt başlık olan kartın arka planı değişsin, formda hatalı alan varsa çevresindeki çerçeve kızarsın, bir karta fare gelince yanındakiler solsun istiyorsunuz. Yıllardır bunun cevabı aynıydı: bir script yaz, durumu yakala, ebeveyne sınıf ekle, sonra o sınıfı CSS'te karşıla. Kod uzar, sınıf eklemeyi unuttuğunuz bir yerde görünüm bozulur. :has() ile bu işin büyük kısmı doğrudan stil dosyasına geçiyor. Aşağıda bunu küçük örneklerle, çalışır halde adım adım gösteriyorum.

1. Adım: :has() Seçicisi Nedir, Hangi Tarayıcılarda Çalışır ve @supports ile Güvenle Nasıl Kullanılır

:has() ilişkisel bir seçicidir ve "içinde şu olan eleman" diye sorar. Düzenli ifadelerdeki ileriye bakma (lookahead) mantığına benzer: seçtiğiniz eleman, parantez içindeki koşulu sağlıyorsa kural uygulanır. Chrome 105 ile geldi, Safari 15.4'ten beri var, Firefox ise 121 sürümüyle (2023 sonu) listeye katıldı. Yani bugün büyük tarayıcıların hepsinde çalışıyor. Yine de hedef kitlenizde eski cihaz çoksa kuralı @supports selector() içine almak ucuz bir sigortadır.

CSS:
/* varsayılan görünüm her tarayıcıda kalır */
.header {
  background-color: #f1f1f1;
}

/* :has() destekleniyorsa devreye girer */
@supports selector(:has(*)) {
  .header:has(h2) {
    background-color: #d3d3d3;
  }
}

İki ayrıntıyı baştan bilin. :has() "affedici" bir seçici listesi değildir: içine virgülle yazdığınız seçicilerden biri geçersizse kuralın tamamı düşer. Ayrıca :has() içinde bir :has() daha kullanamazsınız.

konu-29ed033cb4.jpg

2. Adım: Alt Elemanına Bakarak Ebeveyni Boyamak: h2 Varsa, İkisi Birden Varsa, Biri Varsa ve Hiç Yoksa

En basit kullanım, bir blog kartının başlık alanını içindeki elemanlara göre değiştirmek. Markup şöyle olsun:

HTML:
<header class="header">
  <h1 class="title">Yazı Başlığı</h1>
  <h2 class="subtitle">Alt başlık</h2>
  <blockquote>Kısa bir alıntı</blockquote>
</header>

Dört durumun dördü de tek satırlık seçiciyle karşılanıyor:

CSS:
/* alt başlık varsa */
.header:has(h2) { background-color: darkgrey; }

/* alt başlık ve alıntı birlikte varsa */
.header:has(h2):has(blockquote) { background-color: hotpink; }

/* ikisinden en az biri varsa */
.header:has(h2, blockquote) { background-color: lightsalmon; }

/* alt başlık hiç yoksa */
.header:not(:has(h2)) { background-color: lightpink; }

Dört kuralın özgüllüğü (specificity) aynı olduğu için aynı özelliği değiştirirlerse sonuncusu kazanır. Denerken her seferinde birini bırakın, ya da farklı özellikler (arka plan, kenarlık) kullanın. Virgüllü sürümün "veya", zincirlenen sürümün "ve" anlamına geldiğini aklınızda tutun.

3. Adım: Sayfanın Herhangi Bir Yerini Seçmek: Onay Kutusuyla Bulanıklaştırma ve Hero Bölümü Varsa Menü Logosunu Gizlemek

Seçici ebeveynle sınırlı değil. body üzerinden "sayfada şu varsa, şuranın stilini değiştir" diyebilirsiniz. İlk örnekte bir onay kutusu işaretlenince cevap bulanıklaşıyor, hiç script yok:

CSS:
/* kutu işaretliyse cevabı bulanıklaştır */
body:has(input.blur-answer:checked) .answer {
  filter: blur(5px);
}

/* hero içinde logo varsa menüdeki logoyu gizle */
body:has(.hero-with-logo) .nav-bar .logo {
  visibility: hidden; /* yer kaplar, görünmez */
}

Burada bir düzeltme var. Logoyu gizlemek için opacity: 0 kullanan örnekler görürsünüz. Eleman düzende yerinde kalır diye mantıklı görünse de odak alabilir ve ekran okuyucuya okunabilir. visibility: hidden yerini korur, erişilebilirlik ağacından da çıkarır. Bulanıklaştırma ise yalnızca görsel bir efekttir, ekran okuyucu "42"yi yine okur. Gerçekten saklanması gereken bir bilgi için bu yöntem yetmez.

konu-a75b1f2358.jpg

4. Adım: Önceki Kardeşi Seçmek ve Form Hatalarını :user-invalid ile Etiket ve Fieldset Üzerinde Göstermek

CSS'te şimdiye kadar yalnızca sonraki kardeşi seçebiliyorduk. :has() bunu tersine çeviriyor. *:has(+ .select-before), "hemen ardından .select-before gelen her elemanı seç" demek. Bunun en kullanışlı hali form doğrulamasında:

HTML:
<label for="url-input">Adres girin:</label>
<input type="url" id="url-input" />

CSS:
/* input hatalıysa hemen öncesindeki label kızarsın */
label:has(+ #url-input:user-invalid) { color: crimson; }

/* input label'ın içindeyse */
label:has(input:user-invalid) { background-color: #fde2e2; }

/* gruptaki herhangi bir alan hatalıysa */
fieldset:has(input:user-invalid) { border: 1px solid red; }

:invalid yerine :user-invalid seçmemin nedeni şu: ilki sayfa açılır açılmaz boş zorunlu alanları kırmızıya boyar, ikincisi kullanıcı alanla etkileşime girdikten sonra devreye girer. Tarayıcının kendi doğrulaması (type="url", required, minlength) çalışmaya devam ediyor, siz yalnızca görünümü bağlıyorsunuz.

5. Adım: Slayt Düzenlerini İçeriğe Göre Değiştirmek, Hover'da Diğer Kartları Soluklaştırmak ve Çocuk Sayısına Göre Grid Kurmak

Slayt benzeri article bloklarında düzeni içeriğe göre seçebilirsiniz. Burada bir hata düzeltmem gerekiyor: bu örneklerde > is:(h1, h2) gibi yazılmış, geçersiz bir seçici dolaşıyor. Doğrusu :is(h1, h2). Ayrıca h2 + img kuralı iki görselli slaytı da yakaladığı için :last-child ile daralttım.

CSS:
/* tek başına başlık olan slayt */
article:has(> :is(h1, h2):only-child) {
  grid-template-areas: "heading heading";
}

/* başlık ve tek görsel */
article:has(> h2 + img:last-child) {
  grid-template-areas: "heading heading" "image1 image1";
}

/* başlık ve iki görsel */
article:has(> h2 + img + img) {
  grid-template-areas: "heading heading" "image2 image3";
}

Sıradaki iki örnek liste davranışı. Birincisinde fareyle üzerine gelmediğiniz kartlar sönüyor, ikincisinde üç ve üzeri çocuğu olan liste ızgaraya dönüyor:

CSS:
/* sadece fare destekleyen cihazlarda */
@media (hover: hover) {
  .card-list:has(.card:hover) .card:not(:hover) {
    opacity: 0.4;
    scale: 0.9;
  }
}

/* üçüncü çocuk varsa ızgaraya geç */
.card-list:has(> :nth-child(3)) {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}
.card-list:has(> :nth-child(3)) > :last-child {
  grid-column: 1 / -1;
}

Hover örneğini @media (hover: hover) içine aldım, çünkü dokunmatik ekranda "hover" takılı kalıp kartları soluk bırakabiliyor. Aynı fikir tablo satırlarında da işe yarar.

konu-1b38594387.jpg

6. Adım: Boş Elemanları Yakalamak, Açılır Menü Öğelerini İşaretlemek ve PNG Görselin Arka Planını Ayarlamak

Üç küçük ama işe yarar örnek daha:

CSS:
/* içi boş doğrudan çocuğu olan kutuyu çerçevele */
article:has(> :empty) {
  border: 1px dotted crimson;
}

/* alt menüsü olan öğeye ok ekle */
.menu-dropdown li:has(ul) > a::after {
  content: " \25BE";
}

/* PNG görselin saydam zeminine renk ver */
.display:has(img[src$=".png"]) img {
  background: linear-gradient(90deg, #e8e7e8, #c9ccd0);
}

Boş eleman örneği özellikle React gibi bir kütüphaneyle çalışırken, bileşen farkında olmadan boş bir p üretince hata ayıklamayı kolaylaştırıyor. İçinde boşluk karakteri bulunan eleman :empty sayılmaz, bunu aklınızda tutun. Menü okunda kaynak örnekler emoji kullanıyor. Ben yerine \25BE kaçış kodunu koydum, çünkü emoji fontu cihazdan cihaza değişir. Menüdeki a etiketini kendi markup'ınıza göre uyarlayın.

7. Adım: Canlıya Almadan Önce Performans, Erişilebilirlik ve Tarayıcı Kontrol Listesi

Yayına çıkmadan şu listeden geçin:

  1. Seçiciyi dar tutun. body:has() ve *:has() her DOM değişiminde geniş bir alanı yeniden değerlendirebilir. Mümkünse hedefe en yakın ortak ataya bağlayın, büyük listelerde DevTools Performance sekmesiyle ölçün.
  2. Bilgiyi yalnızca görsel efektle taşımayın. Kırmızı çerçeve ya da blur, ekran okuyucu kullananlara bir şey söylemez; hata metni ve aria-describedby ile destekleyin.
  3. Eski tarayıcı ihtimaline karşı kritik düzenleri @supports selector(:has(*)) ile sarın ve varsayılan görünümü bozulmayacak şekilde yazın.
  4. Chrome, Firefox ve Safari'de formu elle deneyin; :user-invalid davranışı etkileşim sırasına göre küçük farklar gösterebilir.
  5. Hover tabanlı kuralları @media (hover: hover) içinde tutun, klavye kullanıcıları için :focus-within karşılığını da ekleyin.
  6. Aynı özelliği değiştiren birden çok :has() kuralı yazdıysanız sıralarını ve özgüllüklerini kontrol edin.

Bir sonraki adım olarak sitenizdeki en uzun doğrulama script'ini bulun ve yalnızca görünümle ilgili kısmını fieldset:has(input:user-invalid) gibi bir kurala taşımayı deneyin. Takıldığınız seçiciyi ya da tarayıcıda farklı davranan bir durumu konunun altına yazarsanız birlikte bakalım.

Sıkça Sorulan Sorular (SSS)
Soru 1: Paylaşılan CSS örneği canlı (production) ortamda kullanılabilir mi?
Evet. Konudaki kod bloğu bellek güvenliği, hata yakalama ve performans kriterleri gözetilerek hazırlanmıştır; kendi proje mimarinize uyarlayarak doğrudan kullanabilirsiniz.

Soru 2: JavaScript'siz Koşullu Stil: CSS:has ile Ebeveyni, Önceki Kardeşi ve Hatalı Form Alanını Seçen Seçiciler Yazmak uygulamasında en sık yapılan hata nedir?
Girdi doğrulamasını (validation) ve zaman aşımı/kaynak sınırlarını varsayılan değerlerde bırakmaktır. Rehberdeki yapılandırma adımlarını eksiksiz uygulayarak olası darboğazları önleyebilirsiniz.
 
Community platform by XenForo® © 2010-2026 XenForo Ltd.
© 2020-2026 Metin2Games - Tüm Hakları Saklıdır.
Geri
Üst