İçeriğe geç
academia.sh

Ders 05 / 26

Öznitelik Seçicileri

Bir özniteliğin varlığına ve değerine göre eşleme; tam eşleme, liste üyeliği, tire önekli eşleme ve alt dizgi eşlemeleri ile harf duyarlılığı işareti.

İçindekiler

Seçiciler şimdiye kadar öge adına, sınıfa, kimliğe ve ağaçtaki konuma baktı. Belgede bir bilgi katmanı daha var: öznitelikler. Bir bağlantının hedef adresi, bir form alanının türü, bir hücreye veri özniteliğiyle iliştirilen ölçüm birimi — hepsi orada durur ve hiçbiri sınıf listesine yazılmamıştır.

Bu ders öznitelik seçicilerini tanımlar. Yedi eşleme biçimi vardır ve aralarındaki farklar dizgi işlemleridir; bu yüzden hepsi çalıştırılarak gösterilebilir.

Yedi Eşleme Biçimi

Öznitelik seçicisi köşeli parantez içinde yazılır. En basit biçimi özniteliğin varlığını sorar; değerine bakmaz.

Yazım Koşul
[ad] öznitelik var mı
[ad="x"] değer tam olarak x mi
[ad~="x"] boşlukla ayrılmış değer listesinde x var mı
[ad|="x"] değer x mi, ya da x- ile mi başlıyor
[ad^="x"] değer x ile mi başlıyor
[ad$="x"] değer x ile mi bitiyor
[ad*="x"] değer x içeriyor mu

Son üçü alt dizgi eşlemesidir ve değeri yapısız bir metin gibi ele alır. Ortadaki ikisi ise değerin bir yapısı olduğunu varsayar: ~= boşlukla ayrılmış liste, |= tire ile bölümlenmiş kod.

Aşağıdaki program yedi biçimi de uygular ve istasyon sayfasından alınmış ögeler üzerinde sınar.

// oznitelik-esle.mjs — yedi oznitelik esleme bicimini uygular
function esle(bicim, ad, deger, oznitelikler, duyarsiz = false) {
  if (!(ad in oznitelikler)) return false;
  let v = oznitelikler[ad];
  let d = deger;
  if (duyarsiz) { v = v.toLowerCase(); d = d.toLowerCase(); }
  switch (bicim) {
    case "var":  return true;                       // [ad]
    case "=":    return v === d;                    // [ad="x"]
    case "~=":   return v.split(/\s+/).includes(d); // [ad~="x"]
    case "|=":   return v === d || v.startsWith(d + "-");
    case "^=":   return d !== "" && v.startsWith(d);
    case "$=":   return d !== "" && v.endsWith(d);
    case "*=":   return d !== "" && v.includes(d);
    default: throw new Error("bilinmeyen bicim: " + bicim);
  }
}

const ogeler = [
  { ad: "a",     oz: { href: "https://veri.ornek/istasyon.pdf", rel: "noopener yardim" } },
  { ad: "a",     oz: { href: "/olcumler", rel: "yardim" } },
  { ad: "a",     oz: { href: "mailto:gozlem@ornek" } },
  { ad: "input", oz: { type: "number", name: "sicaklik", required: "" } },
  { ad: "input", oz: { type: "text", name: "gozlemci" } },
  { ad: "td",    oz: { "data-birim": "derece-C" } },
  { ad: "td",    oz: { "data-birim": "derece" } },
  { ad: "html",  oz: { lang: "tr-TR" } },
];

const sinamalar = [
  ["[required]",              "var", "required", ""],
  ['[type="number"]',         "=",   "type",     "number"],
  ['[rel~="yardim"]',         "~=",  "rel",      "yardim"],
  ['[lang|="tr"]',            "|=",  "lang",     "tr"],
  ['[href^="https://"]',      "^=",  "href",     "https://"],
  ['[href$=".pdf"]',          "$=",  "href",     ".pdf"],
  ['[href*="olcum"]',         "*=",  "href",     "olcum"],
  ['[data-birim|="derece"]',  "|=",  "data-birim", "derece"],
];

for (const [yazim, bicim, ad, deger] of sinamalar) {
  const bulunan = ogeler.filter((o) => esle(bicim, ad, deger, o.oz));
  console.log(`${yazim.padEnd(26)} -> ${bulunan.length}`);
  for (const b of bulunan) {
    const oz = Object.entries(b.oz).map(([k, v]) => `${k}="${v}"`).join(" ");
    console.log(`   <${b.ad} ${oz}>`);
  }
}

// buyuk-kucuk harf duyarliligi
console.log("--- harf duyarliligi ---");
const oge = { type: "NUMBER" };
console.log('[type="number"]   ->', esle("=", "type", "number", oge));
console.log('[type="number" i] ->', esle("=", "type", "number", oge, true));
[required]                 -> 1
   <input type="number" name="sicaklik" required="">
[type="number"]            -> 1
   <input type="number" name="sicaklik" required="">
[rel~="yardim"]            -> 2
   <a href="https://veri.ornek/istasyon.pdf" rel="noopener yardim">
   <a href="/olcumler" rel="yardim">
[lang|="tr"]               -> 1
   <html lang="tr-TR">
[href^="https://"]         -> 1
   <a href="https://veri.ornek/istasyon.pdf" rel="noopener yardim">
[href$=".pdf"]             -> 1
   <a href="https://veri.ornek/istasyon.pdf" rel="noopener yardim">
[href*="olcum"]            -> 1
   <a href="/olcumler" rel="yardim">
[data-birim|="derece"]     -> 2
   <td data-birim="derece-C">
   <td data-birim="derece">
--- harf duyarliligi ---
[type="number"]   -> false
[type="number" i] -> true

Liste Üyeliği ile Alt Dizgi Farkı

[rel~="yardim"] iki bağlantıyı da döndürdü. Birinin rel değeri "noopener yardim", diğerininki "yardim". Tilde işareti değeri boşluklardan bölüp listede arar; bu yüzden sıradan bağımsızdır ve kısmi bir sözcüğe takılmaz.

Aynı sonuç [rel*="yardim"] ile de alınırdı ama bu yazım yanlış eşleşmelere açıktır: rel="yardimci-degil" değeri de eşleşirdi, oysa liste üyeliği yoktur. Bir öznitelik boşlukla ayrılmış anahtar sözcük listesi taşıyorsa ~= yazılır, *= değil.

Aynı ayrım |= için de geçerlidir. [lang|="tr"] yazımı lang="tr" ve lang="tr-TR" değerlerini eşler, lang="trk" değerini eşlemez. Bu, dil etiketlerinin tire ile bölümlenmiş yapısına göre tasarlanmış bir eşlemedir. Çıktının son satırında aynı biçimin veri özniteliğinde de işlediği görülüyor: data-birim="derece" ve data-birim="derece-C" birlikte eşleşti.

Sınıf Seçicisi Bir Öznitelik Seçicisidir

Çıktıdaki ~= davranışı tanıdık gelmelidir. class özniteliği de boşlukla ayrılmış bir listedir ve sınıf seçicisi tam olarak liste üyeliğini sorar. İki yazım eşdeğerdir:

.eksik        { color: #8a1c1c; }
[class~="eksik"] { color: #8a1c1c; }

İkinci yazım pratikte kullanılmaz, ama bir kavramı yerine oturtur: sınıf, dilin ayrıcalıklı bir kavramı değil, bir özniteliğin üzerine konmuş bir kısayoldur. Aynı biçimde #icerik seçicisi de [id="icerik"] ile aynı kümeyi döndürür — özgüllük hesabındaki ağırlıkları farklı olsa da eşleme davranışları aynıdır.

Harf Duyarlılığı ve Boş Değer

Çıktının son iki satırı bir tuzağı gösteriyor. [type="number"] seçicisi, değeri NUMBER olan bir ögeyle eşleşmedi. Öznitelik değerleri genel kural olarak harf duyarlıdır; bazı HTML öznitelikleri için tanımlı bir duyarsızlık vardır, ancak buna bel bağlamak yerine işaretle açıkça bildirmek daha güvenlidir:

input[type="number" i] { text-align: right; }

Kapanış parantezinden önce yazılan i işareti eşlemeyi harf duyarsız yapar. s işareti tersini, açıkça duyarlı eşlemeyi ister.

İkinci tuzak boş dizgidir. Programdaki üç alt dizgi biçiminde d !== "" denetimi bulunuyor ve bu, dilin kuralını yansıtır: [href*=""] yazımı hiçbir ögeyle eşleşmez. Sezgi tersini söyler — her dizgi boş dizgiyi içerir — ama tanım bu durumu dışarıda bırakır. Bir değeri dinamik olarak seçiciye yerleştiren bir üretim varsa, boş değer durumunda kuralın sessizce ölmesi beklenmelidir.

Öznitelik Seçicisi Ne Zaman Doğru Araçtır

Bir sınıf eklemek her zaman mümkündür; öyleyse öznitelik seçicisi neden gereksin?

Öznitelik seçicisi, ayrımın zaten belgede olduğu durumlarda doğru araçtır. Bir form alanının türü type özniteliğinde yazılıdır; ona ayrıca class="sayi-alani" eklemek aynı bilgiyi iki yere yazmak ve ikisini eşzamanlı tutma yükümlülüğü almaktır. İki kaynak ayrıştığında hangisinin doğru olduğu belirsizleşir.

Aynı gerekçe zorunluluk durumu için de geçerlidir:

/* istasyon.css — 4. adım: form alanları ve dış bağlantılar */
input[type="number"]  { text-align: right; }
input[required]       { border-inline-start: 3px solid #8a5b00; }
a[href^="http"]       { padding-inline-end: 1em; }
td[data-birim|="derece"] { font-variant-numeric: tabular-nums; }

İkinci kural, zorunlu alanları belgedeki required özniteliğinden okur. Alan zorunlu olmaktan çıkarıldığında öznitelik silinir ve biçem kendiliğinden düşer; hiçbir sınıf güncellemesi gerekmez. Doğrulama davranışı ile görsel işaret aynı tek kaynaktan beslenir.

Üçüncü kuralın gerekçesi farklıdır: dış bağlantılara yer açar. Adresin http ile başlaması bir yazar kararı değil, adresin kendi biçimidir; onu ayrıca sınıfla bildirmek gereksiz tekrar olurdu.

Sınırı da söylemek gerekir. Öznitelik seçicisi, ayrım belgede yoksa uydurulamaz. “Bu paragraf uyarıdır” bilgisi hiçbir özniteliğe yazılı değilse, onu taşıyacak yer bir sınıf adıdır.

Özet

  • Öznitelik seçicisi köşeli parantezle yazılır; en basit biçimi özniteliğin varlığını sorar, kalan altı biçim değeri sınar.
  • ~= değeri boşluklardan bölüp liste üyeliği arar, |= tire ile bölümlenmiş kodun önekini eşler; ^= $= *= ise değeri yapısız bir metin gibi ele alır.
  • Sınıf seçicisi [class~="…"] ile, kimlik seçicisi [id="…"] ile aynı kümeyi döndürür; ikisi de öznitelik eşlemesinin kısayoludur.
  • Öznitelik değerleri harf duyarlı eşlenir; duyarsızlık i işaretiyle açıkça istenir. Boş dizgiyle yazılan alt dizgi eşlemeleri hiçbir ögeyle eşleşmez.
  • Ayrım belgede zaten bir öznitelikte duruyorsa öznitelik seçicisi yazılır; aynı bilgiyi bir de sınıfa yazmak iki kaynağı eşzamanlı tutma yükümlülüğü getirir.

Sonraki Adım

Seçicilerin tümü şimdiye kadar belgede yazılı olan bir şeye baktı: ad, sınıf, kimlik, öznitelik, konum. Belgede yazılı olmayan şeyler de vardır — bir bağlantının üzerinde durulup durulmadığı, bir alanın odakta olup olmadığı, bir satırın kaçıncı sırada bulunduğu. Sonraki ders bu durumları seçen sözde sınıfları ve belgede hiç bulunmayan içeriği üreten sözde elemanları tanımlar.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat