Ders 09 / 25
Akordeon
Aşamalı gösterimin kalıbı, yerel açılıp kapanan panel ögesiyle elle kurulan akordeonun karşılaştırılması, genişleme durumunun izlenmesi ve yapısal kural denetimi.
İçindekiler
Sekme kalıbı bir kısıt taşıyordu: aynı anda yalnız bir bölüm görünür. Kayıt ayrıntısı sayfasının alt kısmındaki “Baskılar”, “Konu başlıkları” ve “Benzer kayıtlar” bölümleri için bu kısıt gereksizdir. Kullanıcı üçünü birden açık tutmak isteyebilir; hiçbirini açmadan da sayfayı okuyup ayrılabilir.
Kısıtsız çözüm akordeondur ve tek bir durum bildirimine dayanır: bölüm şu anda genişletilmiş mi? Bu ders o tek bildirimi belirtir ve güncellenmemesinin neden en sık üretilen kusur olduğunu gösterir.
Kalıbın Çözdüğü Sorun
Akordeon, ikincil içeriğin sayfayı uzatmasını engeller. Ölçüt ikincillıktir: kullanıcıların çoğu o bölümü açmadan işini tamamlıyorsa bölüm katlanabilir.
Ölçüt sağlanmadığında kalıp zarar verir. Kaydın raf konumu, ödünç alınabilirlik durumu ve iade tarihi zorunlu bilgilerdir; katlandıklarında kullanıcı bir tuş vuruşu daha yapmak zorunda kalır ve bilginin var olduğunu göremez.
İki ek sınır vardır. Kapalı bir bölümün içeriği belgede gizlidir; kullanıcının sayfa içinde arayarak bulmasını gerektiren bilgi akordeona konmaz. İkincisi, iki ya da üç kısa paragraflık içeriği katlamak kazanç sağlamaz — açma eylemi, kazanılan yerden daha pahalıdır.
Yerel Öge Önce
Bu kalıbın yerel karşılığı vardır: details ve summary ögeleri. summary ögesi
açma denetimidir, details ögesi kapsayıcıdır ve açık olup olmadığı open özniteliğinden
okunur.
Getirdikleri şunlardır: açma ve kapama davranışı, denetimin odaklanabilirliği, giriş ve
boşluk tuşlarıyla çalıştırılabilirlik ve genişleme durumunun ağaca bildirimi. Hiçbiri
ayrıca yazılmaz. details ögelerine ortak bir ad verildiğinde aynı anda yalnız birinin
açık kalması da tanımlıdır; bu davranış gerekiyorsa yetenek saptamasıyla sınanır ve
karşılanmadığında elle kurulur.
Bedeli iki yerdedir: açma imleyicisinin görünümünün sunum katmanından denetlenmesi sınırlıdır ve açılma hareketine geçiş uygulanması kolay değildir. Katalogun kayıt ayrıntısı sayfasında bu bedel ödenebilir.
Elle Kurulan Akordeon
Yerel ögenin karşılamadığı bir gereksinim varsa kalıp bir düğmeyle kurulur.
<h3> <button aria-expanded="true" aria-controls="panel-baski" id="baslik-baski"> Baskılar </button> </h3> <div id="panel-baski" role="region" aria-labelledby="baslik-baski">…</div> <h3> <button aria-expanded="false" aria-controls="panel-konu" id="baslik-konu"> Konu başlıkları </button> </h3> <div id="panel-konu" role="region" aria-labelledby="baslik-konu" hidden>…</div>
Üç karar bağlayıcıdır. Düğme bir başlık ögesinin içindedir; başlık düğmenin içine değil, düğme başlığın içine yazılır. Böylece bölüm adı ana hatta girer ve kullanıcı başlık listesinden bölümler arasında gezinebilir. Başlık düzeyi, çevresindeki ana hatta göre seçilir; akordeonun kendisi bir düzey dayatmaz.
aria-expanded durumu düğmeye yazılır, panele değil: genişleten şey düğmedir. Panelin
role="region" bildirimi ve aria-labelledby bağı, bölümün yer imi listesinde
bulunabilmesini sağlar; bölüm sayısı çoksa bu liste kalabalıklaşır ve bildirim
uygulanmayabilir.
Açma imleyicisi olarak kullanılan ok simgesi sunumsaldır; durumu simge değil,
aria-expanded bildirir.
Genişleme Durumunun İzlenmesi
// akordeon.mjs — genisleme durumunun izlenmesi ve yapisal kural denetimi const BOLUMLER = ["Baskılar", "Konu başlıkları", "Benzer kayıtlar"]; // kip: "coklu" (bagimsiz) | "tekli" (ayni anda bir bolum) const baslangic = (kip) => ({ kip, acik: new Set() }); function calistir(d, i) { const a = new Set(d.acik); if (a.has(i)) a.delete(i); else if (d.kip === "tekli") { a.clear(); a.add(i); } else a.add(i); return { ...d, acik: a }; } // Isaretlemenin uretecegi durum: dugmede aria-expanded, panelde hidden const uretilen = (d) => BOLUMLER.map((ad, i) => ({ ad, "aria-expanded": d.acik.has(i), panelGorunur: d.acik.has(i) })); function iz(kip, dizi) { let d = baslangic(kip); console.log(`\n${kip} kip:`); console.log(" calistirilan".padEnd(22) + "aria-expanded".padEnd(24) + "gorunur panel"); console.log(" " + "(baslangic)".padEnd(20) + uretilen(d).map((o) => (o["aria-expanded"] ? "1" : "0")).join(" ").padEnd(24) + uretilen(d).filter((o) => o.panelGorunur).length); for (const i of dizi) { d = calistir(d, i); const u = uretilen(d); console.log(" " + BOLUMLER[i].padEnd(20) + u.map((o) => (o["aria-expanded"] ? "1" : "0")).join(" ").padEnd(24) + u.filter((o) => o.panelGorunur).length); } return d; } iz("coklu", [0, 1, 2, 1]); const tekli = iz("tekli", [0, 1, 2, 2]); // Degismez: bildirilen durum ile gorunurluk her zaman ortusur; tekli kipte en cok bir panel console.log("\ndegismez taramasi:"); let durum = 0, kirilma = 0; for (const kip of ["coklu", "tekli"]) { const gorulen = new Set(); const kuyruk = [baslangic(kip)]; const anahtar = (d) => [...d.acik].sort().join(","); gorulen.add(anahtar(kuyruk[0])); while (kuyruk.length) { const d = kuyruk.shift(); durum++; const u = uretilen(d); if (u.some((o) => o["aria-expanded"] !== o.panelGorunur)) kirilma++; if (kip === "tekli" && u.filter((o) => o.panelGorunur).length > 1) kirilma++; for (let i = 0; i < BOLUMLER.length; i++) { const y = calistir(d, i); if (gorulen.has(anahtar(y))) continue; gorulen.add(anahtar(y)); kuyruk.push(y); } } } console.log(` taranan durum: ${durum}, degismez kirilmasi: ${kirilma}`); console.log(` tekli kipte ulasilan durum sayisi: ${BOLUMLER.length + 1}`); // --- Yapisal kural denetimi ------------------------------------------------- // Her kayit: baslik dugmesinin sarildigi oge, aria-controls hedefi, panel gizliligi const ISARETLEME = [ { ad: "Baskılar", sarmalayan: "h3", controls: "panel-baski", genisletilmis: true, panelGizli: false, paneller: ["panel-baski"] }, { ad: "Konu başlıkları", sarmalayan: "div", controls: "panel-konu", genisletilmis: false, panelGizli: true, paneller: ["panel-konu"] }, { ad: "Benzer kayıtlar", sarmalayan: "h3", controls: "panel-benzer-2", genisletilmis: true, panelGizli: true, paneller: ["panel-benzer"] }, ]; const BASLIK_OGELERI = new Set(["h1", "h2", "h3", "h4", "h5", "h6"]); const VAR_OLAN = new Set(["panel-baski", "panel-konu", "panel-benzer"]); console.log("\nyapisal kural denetimi:"); let bulgu = 0; for (const k of ISARETLEME) { const b = []; if (!BASLIK_OGELERI.has(k.sarmalayan)) b.push(`baslik dugmesi bir baslik ogesi icinde degil (${k.sarmalayan})`); if (!VAR_OLAN.has(k.controls)) b.push(`aria-controls var olmayan bir kimlige isaret ediyor (${k.controls})`); if (k.genisletilmis === k.panelGizli) b.push(`aria-expanded=${k.genisletilmis} ama panel ${k.panelGizli ? "gizli" : "gorunur"}`); for (const x of b) { bulgu++; console.log(" " + k.ad.padEnd(20) + x); } } console.log(`\n${ISARETLEME.length} bolumde ${bulgu} bulgu`);
coklu kip: calistirilan aria-expanded gorunur panel (baslangic) 0 0 0 0 Baskılar 1 0 0 1 Konu başlıkları 1 1 0 2 Benzer kayıtlar 1 1 1 3 Konu başlıkları 1 0 1 2 tekli kip: calistirilan aria-expanded gorunur panel (baslangic) 0 0 0 0 Baskılar 1 0 0 1 Konu başlıkları 0 1 0 1 Benzer kayıtlar 0 0 1 1 Benzer kayıtlar 0 0 0 0 degismez taramasi: taranan durum: 12, degismez kirilmasi: 0 tekli kipte ulasilan durum sayisi: 4 yapisal kural denetimi: Konu başlıkları baslik dugmesi bir baslik ogesi icinde degil (div) Benzer kayıtlar aria-controls var olmayan bir kimlige isaret ediyor (panel-benzer-2) Benzer kayıtlar aria-expanded=true ama panel gizli 3 bolumde 3 bulgu
Bulguların Okunması
İki izin karşılaştırılması, iki kipin durum uzayı farkını gösteriyor. Çoklu kipte üç bölüm sekiz duruma ulaşır: her bölüm bağımsız olarak açık ya da kapalıdır. Tekli kipte ulaşılan durum sayısı dörttür — üç bölüm ya da hiçbiri. Tarama toplamı on iki, ikisinin toplamıdır.
Tekli kipin izindeki üçüncü satır, kalıbın sık atlanan sonucunu gösteriyor: kullanıcı ikinci bölümü açtığında birincisi kendiliğinden kapanıyor. Ekranda bu, sayfanın kaydığı ve okunan yerin kaybolduğu anlamına gelir. Kapanma bir duyuru üretmez; kullanıcı yalnız açtığını duyar. Bu yüzden tekli kip, yalnız bölüm sayısı çok ve içerikler uzun olduğunda seçilir.
Yapısal denetim üç bulgu üretiyor ve üçü de ekranda görünmez.
Başlık ögesi eksikliği. İkinci bölümün açma düğmesi bir div içinde. Ekranda hiçbir
fark yoktur; ana hatta ise o bölüm bulunmaz ve başlık listesinden gezinen kullanıcı üç
bölümden yalnız ikisini görür.
Kopuk denetim başvurusu. Üçüncü bölümün aria-controls değeri var olmayan bir kimliğe
işaret ediyor; bildirim sessizce düşer.
Bildirim ile görünürlüğün ayrışması. Aynı bölüm genişletilmiş olarak bildiriliyor ama paneli gizli. Bu, kalıbın en pahalı kusurudur: kullanıcıya bölümün açık olduğu söylenir, açıldığında hiçbir içerik bulunmaz ve kullanıcı hatanın kendisinde olduğunu düşünür.
Klavye Sözleşmesi
| Tuş | Davranış |
|---|---|
Tab |
Her başlık düğmesi ayrı bir sekme durağıdır. |
Enter / Boşluk |
Bölümü açar ya da kapatır. |
| Ok tuşları | İsteğe bağlıdır; kurulursa başlıklar arasında gezinir, bölüm açmaz. |
Sekme kalıbından ayrıldığı nokta buradadır. Sekme şeridi tek bir seçim taşıdığı için dönen
sekme durağı kullanır; akordeonun başlıkları ise bağımsız düğmelerdir ve her biri kendi
durağını tutar. Dönen sekme durağı burada uygulanırsa kullanıcı Tab tuşuyla ikinci bölüme
hiç ulaşamaz.
Bir bölüm açıldığında odak düğmede kalır. Panele taşınmaz: kullanıcı açtığı bölümü
okumadan bir sonrakini açmak isteyebilir ve odağın kaçması bunu engeller. Panelin içine
girmek Tab tuşuyla yapılır.
Ölçülebilir Kısıtlar
4.1.2 Ad, Rol, Değer. Genişleme durumu program yoluyla okunabilmeli ve her değişimde güncellenmelidir.
2.5.8 Hedef Boyutu. Başlık satırının tamamı tıklanabilir alandır; yalnızca ok simgesinin tıklanabilir olduğu bir gerçekleştirim hem hedefi 24 pikselin altına düşürür hem de kullanıcının başlık metnine tıklama beklentisini karşılamaz.
1.4.11 Metin Dışı Kontrast. Bölümleri birbirinden ayıran çizgi bilgi taşır ve 3:1 ölçüsüne girer.
2.4.6 Başlıklar ve Etiketler. Başlık metni bölümün içeriğini tanımlamalıdır; “Ayrıntı” ya da “Diğer” gibi metinler bu ölçütü karşılamaz.
2.3.3 Etkileşimle Tetiklenen Hareket. Açılma hareketine geçiş uygulanıyorsa, hareket azaltma tercihi bildirildiğinde geçiş kaldırılmalıdır; ölçüt AAA düzeyindedir, ancak uzun listelerde art arda açılan bölümlerin hareketi rahatsızlık üretir.
Sık Yapılan Hata
Durum bildiriminin panele yazılması. aria-expanded özniteliği paneli genişleten
denetime yazılır. Panele yazıldığında düğme durumsuz kalır ve kullanıcı düğmeye
odaklandığında bölümün açık mı kapalı mı olduğunu öğrenemez. Saptaması: role değeri düğme
olan her ögede bildirimin bulunup bulunmadığının denetlenmesi.
Başlık düğmesinin bağlantı olarak kurulması. Bölüm açmak bir eylemdir ve adres değiştirmez; bileşen düğmedir.
Kapalı panelin ağaçta bırakılması. Panel yalnız sunum katmanında gizlendiğinde ağaçta kalır; kullanıcı kapalı bölümlerin içeriğini de okur ve durum bildirimiyle çelişir. Saptaması, denetimin üçüncü kuralıdır.
Bütün bölümlerin açık başlatılması. Aşamalı gösterim, sayfayı kısaltmak içindir; hepsi açık başlayan bir akordeon yalnız fazladan bir tuş katmanı ekler. Karar: sayfa yüklendiğinde hangi bölümün açık olacağı belirtimde yazılır.
Özet
- Akordeon ikincil içeriği katlar; zorunlu bilgi, sayfa içinde aranması gereken içerik ve çok kısa bölümler katlanmaz.
- Kalıbın yerel karşılığı vardır ve açma davranışını, odaklanabilirliği, tuş sözleşmesini ve durum bildirimini birlikte getirir.
- Elle kurulduğunda düğme bir başlık ögesinin içine yazılır; başlık düğmenin içine değil. Bu, bölümü ana hatta sokar.
aria-expandedbildirimi paneli genişleten düğmeye yazılır ve görünürlükle her durumda örtüşmelidir.- Akordeonun her başlığı ayrı bir sekme durağıdır; dönen sekme durağı yalnız sekme kalıbına aittir.
- Tekli kipte bir bölümün açılması diğerini sessizce kapatır ve durum uzayını sekizden dörde indirir; bu davranış yalnız uzun içerikli çok bölümlü listelerde seçilir.
Sonraki Adım
Buraya kadarki üç kalıpta açılan içerik sayfanın akışı içinde kaldı: panel bir yer kapladı, altındaki içeriği aşağı itti ve odak kendi başına ilerledi. Katalogun üst çubuğundaki “Hesabım” denetimi ise farklı davranır — açtığı liste sayfanın üstünde durur, altındaki içeriği örter ve dışına tıklandığında kapanır. Bu, odağın kuşatılmasını ve kapanışta bir dönüş noktasına götürülmesini gerektiren ilk kalıptır. Sonraki ders menüleri ve açılır panelleri belirtir; ikisinin ayrımını ve odak tuzağının hangi noktada oluştuğunu ele alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.