İçeriğe geç
academia.sh

Ders 08 / 27

Anlamsal İşaretlemenin Rolü

Erişilebilirlik ağacının belge ağacından türetilmesi, yerel ögelerin ürettiği örtük roller, erişilebilir adın hesaplanma sırası ve anlamsız kapsayıcıyla ödenen bedelin sayımı.

İçindekiler

Önceki ders 4.1.2 ölçütünü listeye aldı: bir arayüz bileşeninin adı, rolü ve değeri program yoluyla okunabilmelidir. Ölçütün sınanabilir olması, bu üçlünün bir yerde gerçekten bulunmasına bağlıdır. Bir düğmenin adı sayfanın görünümünde değil, belgeden türetilen ikinci bir yapıda durur.

O yapı, Anlamsal İşaretleme ve Medya konusunda tanıtılan erişilebilirlik ağacıdır. Bu ders ağacın nasıl türetildiğini kural düzeyinde ele alır: hangi düğüm ağaca girer, girenin rolü nereden gelir ve adı hangi sırayla hesaplanır. Üçünün de yanıtı işaretlemededir; hiçbiri sunum katmanından okunmaz.

Ağaç Ağaçtan Türer

Tarayıcı, Belge Nesne Modeli ağacını dolaşarak ikinci bir ağaç kurar. İkinci ağacın düğümleri ögelerin kendisi değil, onların erişilebilirlik karşılıklarıdır: her düğüm bir rol, bir ad, bir açıklama ve bir durum kümesi taşır.

Türetme sırasında bazı düğümler ağaca hiç girmez. Budama kuralları şunlardır: sunum katmanında görüntülenmeyen ya da görünürlüğü kaldırılmış ögeler girmez; hidden özniteliği taşıyan ögeler girmez; aria-hidden="true" bildirimi taşıyan öge ve bütün alt ağacı girmez; alternatif metni boş bırakılmış bir görsel sunumsal sayılarak girmez.

Buradan bir ayrım çıkar. Bir ögeyi ekrandan kaldırmanın iki yolu vardır ve ikisi ağaçta farklı sonuç verir. Ögeyi sunum katmanında görüntüleme dışı bırakmak onu ağaçtan da siler; ögeyi ekranın dışına taşımak ya da saydamlaştırmak ağaçta bırakır. Atlama bağlantısının görünmeden çalışabilmesi ikinci yola dayanır: bağlantı ağaçtadır, odak alır, yalnızca göze görünmez.

Örtük Rol

Ağaca giren her düğümün bir rolü vardır ve bu rol, hiçbir bildirim yazılmadığında ögenin kendisinden gelir. Buna örtük rol (implicit role) denir. Karşılıkların bir bölümü koşulludur: aynı öge, bir özniteliğe göre farklı rol üretir.

a ögesi yalnızca bir href özniteliği taşıdığında bağlantı rolü üretir; taşımadığında ağaçta anlamsız bir düğümdür. input ögesinin rolü tür özniteliğinden gelir: metin alanı, arama alanı, sayı alanı ve onay kutusu ayrı rollerdir. th ögesinin rolü kapsam özniteliğine göre sütun başlığı ya da satır başlığıdır. Bölüm ögelerinin yer imi rollerine karşılığı ve o karşılıkların koşulları Web’in Temelleri ve HTML kursunda kurulmuştu; burada aynı türetme, denetim ögeleri için sürdürülüyor.

Erişilebilir Adın Hesabı

Rolden sonraki soru addır. Erişilebilir ad (accessible name), bir düğümü kullanıcıya tanıtan dizgidir ve tek bir kaynaktan gelmez: tanımlı bir öncelik sırasıyla hesaplanır. Aşağıdaki betik sıranın yerel bölümünü uygular. ARIA kaynakları sıranın en başında durur ve sonraki dersin konusudur; burada onlar olmadığında ne olduğu görülüyor.

// ad.mjs — yerel ogelerin ortuk rolu ve erisilebilir adin hesabi (ARIA kaynaklari haric)

function rol(d) {
  const o = d.oz ?? {};
  switch (d.oge) {
    case "a": return o.href === undefined ? "generic" : "link";
    case "button": return "button";
    case "input":
      return { checkbox: "checkbox", radio: "radio", number: "spinbutton",
               search: "searchbox", submit: "button" }[o.type ?? "text"] ?? "textbox";
    case "select": return "combobox";
    case "table": return "table";
    case "th": return o.scope === "row" ? "rowheader" : "columnheader";
    case "td": return "cell";
    case "tr": return "row";
    case "fieldset": return "group";
    case "img": return o.alt === "" ? "— (sunumsal)" : "image";
    case "ul": return "list";
    case "li": return "listitem";
    case "h1": case "h2": case "h3": return "heading";
    case "output": return "status";
    default: return "generic";
  }
}

// ARIA'da icerikten ad alan roller
const ICERIKTEN = new Set(["button", "link", "heading", "cell", "columnheader",
  "rowheader", "listitem", "checkbox", "radio", "option"]);

function metin(d) {
  if (typeof d === "string") return d;
  const o = d.oz ?? {};
  if (o.hidden) return "";
  if (d.oge === "img") return typeof o.alt === "string" ? o.alt : "";
  return (d.cocuklar ?? []).map(metin).join(" ").replace(/\s+/g, " ").trim();
}

// Sira: yerel kaynak -> icerik metni (izin veren roller) -> son care
function ad(d, etiketler) {
  const o = d.oz ?? {};
  if (o.hidden) return ["", "gizli"];
  if (d.oge === "img" && typeof o.alt === "string" && o.alt !== "") return [o.alt, "alt"];
  for (const [oge, alt] of [["table", "caption"], ["fieldset", "legend"], ["figure", "figcaption"]]) {
    if (d.oge !== oge) continue;
    const c = (d.cocuklar ?? []).find((x) => x.oge === alt);
    if (c) return [metin(c), alt];
  }
  if (["input", "select", "textarea"].includes(d.oge)) {
    if (etiketler[o.id]) return [etiketler[o.id], "label ogesi"];
    if (o.title) return [o.title, "title (son care)"];
    if (o.placeholder) return [o.placeholder, "placeholder (son care)"];
    return ["", "ad yok"];
  }
  if (ICERIKTEN.has(rol(d))) {
    const m = metin(d);
    if (m) return [m, "icerik metni"];
  }
  if (o.title) return [o.title, "title (son care)"];
  return ["", "ad yok"];
}

// --- Kuzey Yamac: suzgec paneli ve olcum tablosundan ornekler ----------------
const ETIKET = {
  "suz-kod": "İstasyon kodu",
  "suz-uyari": "Yalnız uyarılı ölçümler",
  deger: "Ölçülen değer (°C)",
};

const ORNEK = [
  ["<button>Süz</button>", { oge: "button", cocuklar: ["Süz"] }],
  ['<button><img alt="Süzgeci temizle"></button>',
   { oge: "button", cocuklar: [{ oge: "img", oz: { alt: "Süzgeci temizle" } }] }],
  ['<button><img alt=""></button>',
   { oge: "button", cocuklar: [{ oge: "img", oz: { alt: "" } }] }],
  ['<input id="suz-kod" type="search"> + label',
   { oge: "input", oz: { id: "suz-kod", type: "search" } }],
  ['<input type="search" placeholder="istasyon">',
   { oge: "input", oz: { id: "ara", type: "search", placeholder: "istasyon" } }],
  ['<input id="deger" type="number"> + label',
   { oge: "input", oz: { id: "deger", type: "number" } }],
  ['<input id="suz-uyari" type="checkbox"> + label',
   { oge: "input", oz: { id: "suz-uyari", type: "checkbox" } }],
  ["<table><caption>Günlük ölçümler</caption>",
   { oge: "table", cocuklar: [{ oge: "caption", cocuklar: ["Günlük ölçümler"] }] }],
  ["<fieldset><legend>Ölçüm kaynağı</legend>",
   { oge: "fieldset", cocuklar: [{ oge: "legend", cocuklar: ["Ölçüm kaynağı"] }] }],
  ['<a href="/istasyonlar/">İstasyonlar</a>',
   { oge: "a", oz: { href: "/istasyonlar/" }, cocuklar: ["İstasyonlar"] }],
  ["<a>İstasyonlar</a>", { oge: "a", cocuklar: ["İstasyonlar"] }],
  ['<div class="dugme">Kaydet</div>', { oge: "div", cocuklar: ["Kaydet"] }],
  ['<th scope="col">Sıcaklık</th>',
   { oge: "th", oz: { scope: "col" }, cocuklar: ["Sıcaklık"] }],
  ['<img src="uyari.svg" alt="">', { oge: "img", oz: { alt: "" } }],
];

console.log("isaretleme".padEnd(48) + "rol".padEnd(14) + "erisilebilir ad".padEnd(27) + "kaynak");
for (const [kaynak, dugum] of ORNEK) {
  const [a, nereden] = ad(dugum, ETIKET);
  console.log(
    kaynak.padEnd(48) + rol(dugum).padEnd(14) +
    (a === "" ? "(bos)" : '"' + a + '"').padEnd(27) + nereden,
  );
}
isaretleme                                      rol           erisilebilir ad            kaynak
<button>Süz</button>                            button        "Süz"                      icerik metni
<button><img alt="Süzgeci temizle"></button>    button        "Süzgeci temizle"          icerik metni
<button><img alt=""></button>                   button        (bos)                      ad yok
<input id="suz-kod" type="search"> + label      searchbox     "İstasyon kodu"            label ogesi
<input type="search" placeholder="istasyon">    searchbox     "istasyon"                 placeholder (son care)
<input id="deger" type="number"> + label        spinbutton    "Ölçülen değer (°C)"       label ogesi
<input id="suz-uyari" type="checkbox"> + label  checkbox      "Yalnız uyarılı ölçümler"  label ogesi
<table><caption>Günlük ölçümler</caption>       table         "Günlük ölçümler"          caption
<fieldset><legend>Ölçüm kaynağı</legend>        group         "Ölçüm kaynağı"            legend
<a href="/istasyonlar/">İstasyonlar</a>         link          "İstasyonlar"              icerik metni
<a>İstasyonlar</a>                              generic       (bos)                      ad yok
<div class="dugme">Kaydet</div>                 generic       (bos)                      ad yok
<th scope="col">Sıcaklık</th>                   columnheader  "Sıcaklık"                 icerik metni
<img src="uyari.svg" alt="">                    — (sunumsal)  (bos)                      ad yok

Tablonun okunması gereken üç yeri var.

İçerikten ad yalnız bazı rollerde alınır. Betikteki küme bunu uyguluyor: düğme, bağlantı, başlık ve hücre rolleri adını içindeki metinden alır; metin alanı, tablo ve grup rolleri almaz. Bu yüzden İstasyonlar metni bağlantı içinde ad üretiyor, aynı metin href taşımayan bir a ögesinde hiçbir şey üretmiyor. Ayrım keyfi değildir: bir metin alanının içindeki metin değeridir, adı değildir; ikisi karışırsa kullanıcının yazdığı şey alanın adı sanılır.

İçerik gezinmesi alt ögelerin adını toplar. Süzgeci temizleyen düğmenin içinde metin yok, bir görsel var; görselin alternatif metni düğmenin adı oluyor. Üçüncü satır bunun ters yüzüdür: aynı düğmede alternatif metin boş bırakıldığında görsel sunumsal sayılıp budanıyor ve geriye adsız bir düğme kalıyor. Ekranda simge görünür, ağaçta ad yoktur. Bu, simge düğmelerinde en sık üretilen kusurdur ve 4.1.2 ölçütünü doğrudan kaldırır.

Son çare kaynakları ada dönüşür ama iş görmez. Yer tutucu metni olan arama alanı bir ad kazanıyor. Bu ad, alan doldurulduğunda ekranda kaybolan bir metindir ve Karşıtlık ve Erişilebilirlik dersinde ölçülen düşük karşıtlığı da taşır. Hesabın onu ad olarak kabul etmesi, tasarımın onu etiket yerine koyabileceği anlamına gelmez. Aynı şey title özniteliği için de geçerlidir: hesapta bulunur, işaret aygıtına bağlıdır ve kalıcı bir etiketin yerini tutmaz.

Değer ve Durum

Üçlünün son ögesi değerdir ve rolüne göre değişir: metin alanının değeri yazılan metin, onay kutusunun değeri işaretli olup olmaması, sayı alanının değeri sayının kendisi ve alt–üst sınırlarıdır. Bunların yanında durumlar bulunur: devre dışı, zorunlu, geçersiz, salt okunur, genişletilmiş.

Yerel ögelerde bu bildirimlerin tamamı özniteliklerden gelir ve ayrıca yazılmaz. required yazıldığında zorunluluk ağaca geçer; disabled yazıldığında hem etkinlik durumu bildirilir hem de öge odak dışına çıkar; sayı alanına min ve max yazıldığında değer aralığı ağaçta görünür. Web’in Temelleri ve HTML kursundaki kısıt doğrulaması bu nedenle iki iş birden yapar: tarayıcıya kuralı, erişilebilirlik ağacına durumu bildirir.

Anlamsız Kapsayıcının Bedeli

Ölçüm tablosunun bir satırını silen düğme div ögesiyle kurulduğunda ekranda aynı görünür. Ağaçta ise altı bildirim birden eksilir ve her biri elle yerine konmak zorundadır: rol (düğme olduğu), erişilebilir ad, odaklanabilirlik, boşluk ve giriş tuşlarıyla çalıştırılabilirlik, devre dışı durumunun bildirimi, form gönderimiyle ilişkisi.

Bu altı maddenin beşi işaretlemede tek bir öge adı değiştirilerek geri kazanılır. Yerel öge kullanma tercihi bir üslup kararı değil, yeniden üretilmesi gereken bildirim sayısının sıfıra indirilmesidir. Aynı sayım tablo için de yapılır: div yığınıyla kurulmuş bir çizelgede satır ve sütun ilişkileri, başlık hücrelerinin hangi veriye ait olduğu ve toplam satır sayısı ağaca hiç geçmez; kullanıcı hücreler arasında dolaşırken bulunduğu sütunun adını duyamaz.

Karar ölçütü şudur: yapılacak iş için tanımlı bir öge varsa o kullanılır. Tanımlı öge yoksa —sekmeli bir panel, bir ağaç görünümü, bir birleşik giriş alanı— eksik bildirimlerin elle yazılması gerekir ve bunun aracı ARIA’dır.

Özet

  • Erişilebilirlik ağacı belge ağacından türetilir; görüntülenmeyen, hidden taşıyan, aria-hidden bildirimi olan ve alternatif metni boş görsel düğümler budanır.
  • Örtük rol ögeden gelir ve koşulludur: a ögesi ancak href taşırsa bağlantı, input ögesinin rolü türünden, th ögesinin rolü kapsamından belirlenir.
  • Erişilebilir ad tanımlı bir sırayla hesaplanır; içerikten ad yalnızca izin veren rollerde alınır ve içerik gezinmesi alt ögelerin alternatif metnini toplar.
  • Alternatif metni boş bir simge içeren düğme adsız kalır; yer tutucu ve title hesapta ad üretse de kalıcı etiketin yerini tutmaz.
  • Değer ve durum bildirimleri yerel ögelerde özniteliklerden gelir; anlamsız kapsayıcıyla kurulan bir denetimde rol, ad, odaklanabilirlik, tuş davranışı ve durum elle üretilmelidir.

Sonraki Adım

Son bölüm bir boşluk bıraktı: tanımlı ögesi olmayan bir bileşen nasıl bildirilir? Ölçüm listesinin üstündeki süzgeç paneli açılıp kapanan bir bölümdür, uyarı bildirimleri kendiliğinden belirir, ölçüm tablosu sıralanabilir sütunlar taşır. Bunların hiçbirinin yerel karşılığı yoktur ve üçü de ağaçta doğru görünmek zorundadır. Sonraki ders bu boşluğu dolduran bildirim kümesini —rolleri, durumları ve özellikleri— ele alır; aynı kümenin yanlış kullanıldığında ağacı nasıl bozduğunu da aynı ad hesabı üzerinde gösterir.

İ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