İçeriğe geç
academia.sh

Ders 05 / 24

Odak ve Klavye Etkileşimi

Girdiyi alan tek ögenin belirlenmesi; odaklanabilirlik koşulları, sekme sırasının hesabı, odak olaylarının kabarma davranışı, tuş olaylarının taşıdığı bilgi ve grup içinde tek durak bırakan gezinme düzeni.

İçindekiler

Önceki ders, klavye davranışlarının iptal edilmemesi gereken davranışlar arasında olduğunu söyledi. Gerekçe şudur: sayfayla etkileşimin tamamı işaretleme aygıtıyla kurulmaz. Kullanıcı sekme tuşuyla ögeler arasında dolaşır, boşlukla düğmeye basar, ok tuşlarıyla seçenekler arasında geçer; ekran okuyucu da aynı yolu izler.

Bu dolaşımın merkezinde tek bir kavram vardır: belgede her an en çok bir öge girdiyi alır. O ögenin adı etkin öge (active element) dir ve klavyeden gelen olayların hedefi odur. Bu ders etkin ögenin nasıl belirlendiğini ve klavye olaylarının hangi bilgiyi taşıdığını kurar.

Odaklanabilirlik

Odak (focus), girdiyi alma yetkisidir. Her öge odak alamaz. Bir ögenin odak alabilmesi üç koşula bağlıdır.

Birincisi doğal odaklanabilirliktir. Bağlantılar, düğmeler, form alanları ve denetimler odak alır; bunun için ek bir bildirim gerekmez. Metin taşıyan bir bölüm ya da bir listeye odak, ancak açıkça istendiğinde verilir.

İkincisi görünürlüktür. Belgeden gizlenmiş bir öge odak alamaz; gizli bir ögeye odak vermeye çalışan kod sessizce başarısız olur. Bu, açılır bir panelin kapanma anında odağın kaybolmasının kaynağıdır.

Üçüncüsü etkinliktir. Devre dışı bırakılmış bir denetim odak almaz; klavye kullanıcısı onu hiç görmez. Buradan bir tasarım sonucu çıkar: bir düğmeyi devre dışı bırakmak, onunla ilgili hata iletisini de erişilemez kılabilir. Nedeni ögenin üzerinde duramamaktır.

Odak alan ögenin görsel bir işareti olması zorunludur. WCAG 2.4.7 ölçütü, odaklanmış ögenin görünür bir göstergesi olmasını gerektirir. Odak göstergesini biçemden kaldıran kod, göreni olmayan bir imleçle çalışmaya benzer bir durum üretir.

Sekme Sırası

Sekme tuşu odağı belge sırasına göre taşır. Bu sıra, tabindex özniteliğiyle değiştirilebilir ve değerin işareti üç farklı davranış üretir.

// sekme.mjs — sekme sirasinin belge sirasindan ve tabindex degerinden turetilmesi
const ogeler = [
  { ad: "a.atlama-bagi",      tabindex: null, dogalOdaklanir: true,  gizli: false, devreDisi: false },
  { ad: "input#suzgec",       tabindex: null, dogalOdaklanir: true,  gizli: false, devreDisi: false },
  { ad: "button#suz",         tabindex: null, dogalOdaklanir: true,  gizli: false, devreDisi: false },
  { ad: "button#disaAktar",   tabindex: 1,    dogalOdaklanir: true,  gizli: false, devreDisi: false },
  { ad: "div#ozet",           tabindex: -1,   dogalOdaklanir: false, gizli: false, devreDisi: false },
  { ad: "span.rozet",         tabindex: null, dogalOdaklanir: false, gizli: false, devreDisi: false },
  { ad: "button.sil[T-01]",   tabindex: null, dogalOdaklanir: true,  gizli: false, devreDisi: false },
  { ad: "button.sil[T-02]",   tabindex: null, dogalOdaklanir: true,  gizli: false, devreDisi: true  },
  { ad: "button#gonder",      tabindex: null, dogalOdaklanir: true,  gizli: true,  devreDisi: false },
  { ad: "div.satir-kabi",     tabindex: 0,    dogalOdaklanir: false, gizli: false, devreDisi: false },
];

const odaklanabilir = (o) =>
  !o.gizli && !o.devreDisi && (o.dogalOdaklanir || o.tabindex !== null);
const siradaYerAlir = (o) => odaklanabilir(o) && (o.tabindex === null ? true : o.tabindex >= 0);

const sekmeSirasi = (liste) => {
  const belgeSirasi = liste.map((o, i) => ({ ...o, i })).filter(siradaYerAlir);
  const pozitif = belgeSirasi.filter((o) => (o.tabindex ?? 0) > 0)
    .sort((a, b) => a.tabindex - b.tabindex || a.i - b.i);
  const sifir = belgeSirasi.filter((o) => (o.tabindex ?? 0) === 0);
  return [...pozitif, ...sifir];
};

console.log("sekme sirasi:");
for (const [n, o] of sekmeSirasi(ogeler).entries())
  console.log(`  ${n + 1}. ${o.ad.padEnd(18)} tabindex=${o.tabindex ?? "yok"}`);

console.log("sirada olmayanlar:");
for (const o of ogeler.filter((o) => !siradaYerAlir(o))) {
  const neden = o.gizli ? "gizli" : o.devreDisi ? "devre disi"
    : o.tabindex === -1 ? "tabindex=-1 (yalniz programla odaklanir)" : "odaklanabilir degil";
  console.log(`  ${o.ad.padEnd(18)} ${neden}`);
}
sekme sirasi:
  1. button#disaAktar   tabindex=1
  2. a.atlama-bagi      tabindex=yok
  3. input#suzgec       tabindex=yok
  4. button#suz         tabindex=yok
  5. button.sil[T-01]   tabindex=yok
  6. div.satir-kabi     tabindex=0
sirada olmayanlar:
  div#ozet           tabindex=-1 (yalniz programla odaklanir)
  span.rozet         odaklanabilir degil
  button.sil[T-02]   devre disi
  button#gonder      gizli

Üç kural çıktıda görünür.

Sıfır değeri ögeyi sıraya sokar, yerini değiştirmez. Doğal olarak odak almayan bir kapsayıcı böylece sekme durağı olur ve belge sırasındaki yerini korur.

Eksi bir değeri ögeyi sıradan çıkarır ama odaklanabilir bırakır. Sekme tuşu üzerinden geçmez; program odağı oraya taşıyabilir. Bir hata özetine ya da açılan panele odak vermek bu değerle mümkün olur.

Artı değerler sırayı bozar. Çıktının ilk satırı bunu gösterir: dışa aktarma düğmesi belgede dördüncü sıradadır, sekme sırasında birincidir. Sayfanın atlama bağı ikinci sıraya düşmüştür. Artı değerler belgedeki bütün diğer duraklardan önce gelir; sayfaya sonradan eklenen her bileşen bu sıralamayı yeniden düşünmek zorunda kalır. WCAG 2.4.3 ölçütünün istediği anlamlı sıra, işaretlemenin sırasını doğru kurmakla sağlanır; artı değerlerle değil.

Odak Olayları ve Programlı Odak

Odak değişimi dört olay üretir ve bunlar iki çift hâlinde tanımlıdır. Olay Modeli dersinde belirtildiği gibi odak alma ve odak kaybetme olayları kabarmaz; aynı bilgiyi veren kabaran bir çift ayrıca tanımlıdır. Bir formdaki bütün alanların odak değişimini tek dinleyiciyle izlemek isteyen kod, kabaran çifti kullanmak ya da yakalama kipine kaydolmak zorundadır; Olay Devri dersinde konan kural burada işler.

Odak olayları, odağı bırakan ve alan ögeyi birlikte bildirir. Bir alandan çıkıldığında odağın nereye gittiği bu bilgiyle okunur ve iki durum ayırt edilir: odak formun içinde başka bir alana geçmiştir ya da formun tamamından çıkılmıştır. Alan doğrulamasını yalnızca ikinci durumda çalıştırmak, kullanıcıyı her alan geçişinde hata iletisiyle karşılamaktan kurtarır.

Odağı program taşıdığında iki sorumluluk doğar. Birincisi, odak taşınan ögenin görünmesidir; tarayıcı odaklanan ögeyi görünür alana kaydırır ve bu kaydırma bir sayfa sıçraması olarak algılanabilir. İkincisi, odağın kaybolmamasıdır: odaklı bir öge ağaçtan çıkarıldığında odak belgenin köküne düşer ve sekme tuşu dolaşıma en baştan başlar. Bir satırı silen kod, silmeden önce odağın nereye gideceğine karar vermelidir — komşu satıra ya da listenin kendisine.

Bir üçüncü durum odağı kapatmayı gerektirir. Açılan bir iletişim kutusu, arkasındaki sayfaya sekme tuşuyla geçilmesini engeller; odak kutunun içinde döner. Bu düzenin kurulması sırasında WCAG 2.1.2 ölçütü bir sınır koyar: kullanıcı klavyeyle girdiği yerden klavyeyle çıkabilmelidir. Çıkışı olmayan bir odak kuşatması erişilebilirlik hatasıdır.

Klavye Olayları

Tuş basımı iki olay üretir: tuşa basıldığında ve tuş bırakıldığında. Basılı tutulan tuş, işletim sisteminin tekrar aralığına göre yeni basım olayları üretir; olay nesnesindeki tekrar bayrağı bu olayları ilk basımdan ayırır. Sayaç artıran bir tuş kısayolu, tekrarı saymak isteyip istemediğine göre bu bayrağı okur.

Olay nesnesi tuşu iki ayrı alanla bildirir ve ikisinin ayrımı kritiktir.

Anlam alanı tuşun ürettiği değeri verir: a, Enter, ArrowDown, Escape. Kullanıcının klavye düzenine ve basılı değiştiricilere göre değişir; büyük harf ile küçük harf farklı değerlerdir.

Konum alanı tuşun klavyedeki fiziksel yerini verir. Düzen değişse de aynı kalır.

Seçim kuralı şudur: kısayol bir harfin anlamına dayanıyorsa anlam alanı, tuşun elin altındaki yerine dayanıyorsa konum alanı okunur. Anlam alanına dayanan kod, farklı düzenlerde farklı tuşlara düşer; konum alanına dayanan kod ise kullanıcının gördüğü harfle uyuşmayabilir.

Metin girişini tuş olaylarından türetmek ayrı bir hatadır. Çince, Japonca ve Korece girişte kullanılan birleştirme (composition) düzeneğinde kullanıcı birden çok tuşa basarak tek bir karakter üretir; ara aşamada alanın içeriği tuşlarla örtüşmez. Aynı biçimde yapıştırma, sesle giriş ve otomatik doldurma tuş olayı üretmez. Alanın içeriğini öğrenmenin tek güvenilir yolu, girdi olaylarını dinlemektir; bu Form Olayları dersinin konusudur.

Grup İçi Gezinme

Ölçüm tablosunun her satırında bir düğme varsa, sekme tuşu bütün satırları tek tek dolaşır; yüz satırlık tabloda tablodan çıkmak yüz tuşa bakar. Yerleşik çözüm, grubu tek sekme durağı yapıp grup içinde ok tuşlarıyla gezinmektir. Düzenin adı dönen sekme durağı (roving tabindex) dır: grupta her an tek bir ögenin tabindex değeri sıfırdır, diğerleri eksi birdir; ok tuşu hem odağı hem de sıfır değerini taşır.

// donen.mjs — grup icinde tek sekme duragi birakan odak yonetimi
const satirlar = ["T-01", "T-02", "T-03", "T-04"];
let etkin = 0;          // gruptaki tek sekme duragi
let odak = "grup disi"; // o an odagi tutan oge

const duraklar = () => satirlar.map((ad, i) => `${ad}:${i === etkin ? 0 : -1}`).join(" ");

function tus(ad) {
  const sonuc = { tus: ad, onlendi: false };
  if (odak === "grup disi") {
    if (ad === "Tab") odak = satirlar[etkin];   // gruba giris: tarayicinin kendi davranisi
    return sonuc;
  }
  switch (ad) {
    case "ArrowDown": etkin = (etkin + 1) % satirlar.length; break;
    case "ArrowUp":   etkin = (etkin - 1 + satirlar.length) % satirlar.length; break;
    case "Home":      etkin = 0; break;
    case "End":       etkin = satirlar.length - 1; break;
    case "Tab":       odak = "grup disi"; return sonuc;   // varsayilan davranis birakilir
    default:          return sonuc;                        // is yapilmadi, iptal istenmez
  }
  odak = satirlar[etkin];
  sonuc.onlendi = true;   // ok tuslarinin kaydirma davranisi iptal edilir
  return sonuc;
}

console.log("baslangic:", odak.padEnd(9), "|", duraklar());
for (const ad of ["Tab", "ArrowDown", "ArrowDown", "End", "Home", "ArrowUp", "x", "Tab"]) {
  const sonuc = tus(ad);
  console.log(
    sonuc.tus.padEnd(9),
    "odak:", odak.padEnd(9),
    "| iptal:", String(sonuc.onlendi).padEnd(5),
    "|", duraklar(),
  );
}
baslangic: grup disi | T-01:0 T-02:-1 T-03:-1 T-04:-1
Tab       odak: T-01      | iptal: false | T-01:0 T-02:-1 T-03:-1 T-04:-1
ArrowDown odak: T-02      | iptal: true  | T-01:-1 T-02:0 T-03:-1 T-04:-1
ArrowDown odak: T-03      | iptal: true  | T-01:-1 T-02:-1 T-03:0 T-04:-1
End       odak: T-04      | iptal: true  | T-01:-1 T-02:-1 T-03:-1 T-04:0
Home      odak: T-01      | iptal: true  | T-01:0 T-02:-1 T-03:-1 T-04:-1
ArrowUp   odak: T-04      | iptal: true  | T-01:-1 T-02:-1 T-03:-1 T-04:0
x         odak: T-04      | iptal: false | T-01:-1 T-02:-1 T-03:-1 T-04:0
Tab       odak: grup disi | iptal: false | T-01:-1 T-02:-1 T-03:-1 T-04:0

Dört davranış çıktıda okunur. Gruba giriş tarayıcının kendi sekme davranışıdır ve iptal edilmez. Ok tuşları odağı taşır ve yalnızca bu tuşlarda iptal istenir; ok tuşlarının sayfayı kaydırma davranışı yerine grup içi gezinme konmuştur. Son satırdan önceki x satırı, ele alınmayan tuşlarda iptal istenmediğini gösterir — yazma, kısayollar ve tarayıcı davranışları etkilenmez. Son sekme tuşu grubu terk eder ve gruptaki durak, en son kullanılan satırda kalır; kullanıcı geri döndüğünde bıraktığı yerden devam eder.

Özet

  • Belgede her an en çok bir öge girdiyi alır; odaklanabilirlik doğal odak alma, görünürlük ve etkinlik koşullarına bağlıdır.
  • Sıfır tabindex ögeyi belge sırasındaki yerinde sıraya sokar, eksi bir sıradan çıkarır ama programlı odağa açık bırakır, artı değerler bütün sıranın önüne geçerek düzeni bozar.
  • Odak olaylarının kabarmayan çifti yalnızca hedefte görülür; devir için kabaran çift ya da yakalama kipi kullanılır. Olaylar odağı bırakan ve alan ögeyi birlikte bildirir.
  • Odaklı bir öge ağaçtan çıkarılırsa odak kökten yeniden başlar; silme ve kapatma işlemleri odağın nereye gideceğine önceden karar vermelidir.
  • Tuş olayları anlamı ve fiziksel konumu ayrı alanlarda bildirir; metin girişi tuş olaylarından türetilemez.
  • Dönen sekme durağı düzeninde grupta tek durak bulunur, ok tuşları odağı ve durağı birlikte taşır, ele alınmayan tuşlarda varsayılan davranış korunur.

Sonraki Adım

Bu ders metin girişini öğrenmenin tek güvenilir yolunun tuş olayları olmadığını söyledi ve gerçek yolu sonraya bıraktı. Sayfadaki form yalnızca metin de toplamaz: ölçüm değeri sayıdır, tarih bir takvim denetiminden gelir, istasyon kodu bir listeden seçilir. Her denetim değeri farklı anlarda bildirir — kimi her tuşta, kimi alan terk edildiğinde. Bunun üstünde bir de gönderim vardır: tarayıcı alanları toplar, kısıtları sınar ve isteği kurar. Sonraki ders bu akışı, hangi olayın hangi anı bildirdiğini ve doğrulamanın nereye yerleştiğini inceler.

İ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