Ders 14 / 25
Taşıma
Bir alt ağacın bileşen ağacındaki yerini koruyup belge ağacında başka bir düğüme çizilmesi; kırpma ve yığılma kısıtları, iki ağacın ayrılması, olay yolunun ve bağlam çözümlemesinin bileşen ağacını izlemesi, hedef düğümün koşulları ve erişilebilirlik yükümlülükleri.
İçindekiler
Önceki yedi ders bir alt ağacın ne zaman oluşturulacağını belirledi: durum değiştiğinde, bağımlılık değiştiğinde, hata yakalandığında, veri geldiğinde. Bir soru daha kaldı ve o zamanla değil yerle ilgilidir: alt ağaç nereye çizilir?
Varsayılan yanıt açıktır. Bileşen ağacındaki yer, belge ağacındaki yeri belirler; bir bileşenin ürettiği düğümler, üst bileşeninin düğümlerinin içine girer. Ölçüm istasyonu sayfasında bu varsayılanın işe yaramadığı bir nokta vardır: süzgeç panelindeki açılır liste. Liste mantıksal olarak panelin bir parçasıdır — panelin durumunu okur, panelin olaylarını üretir — ama görsel olarak panelin kutusunun dışına çıkması gerekir.
Bu derste kurulan mekanizmaya taşıma (portal) denir: bileşen ağacındaki yeri koruyup belge ağacındaki yeri değiştirmek.
İki Kısıt: Kırpma ve Yığılma
Sorun bir tercih değil, iki ölçülebilir kısıttır.
// kirpma-ve-yigilma.mjs — tasimanin cozdugu iki gorsel kisit const kes = (a, b) => ({ ust: Math.max(a.ust, b.ust), alt: Math.min(a.alt, b.alt) }); const yukseklik = (r) => Math.max(0, r.alt - r.ust); const acilirListe = { ust: 280, alt: 520 }; // 240 piksel yuksekliginde const suzgecPaneli = { ust: 120, alt: 320 }; // tasan icerigi kirpiyor const gorunumAlani = { ust: 0, alt: 800 }; console.log("kirpma:"); for (const [ad, kirpan] of [["panelin icinde", suzgecPaneli], ["tasinmis (govde koku)", gorunumAlani]]) { const g = kes(acilirListe, kirpan); console.log( ` ${ad.padEnd(22)} gorunen ${String(yukseklik(g)).padStart(3)}/${yukseklik(acilirListe)} piksel`, `(${g.ust}-${g.alt})`, ); } // Yigilma zinciri: kokten baslayarak her yigilma baglaminin z-index degeri. const zincirler = { "acilir-liste (panelin icinde)": [1, 9999], // panel kendi yigilma baglamini kuruyor "acilir-liste (tasinmis)": [9999], "tablo basligi (yapiskan)": [5], }; function karsilastir(a, b) { const n = Math.max(a.length, b.length); for (let i = 0; i < n; i++) { const x = i < a.length ? a[i] : -Infinity; const y = i < b.length ? b[i] : -Infinity; if (x !== y) return x < y ? "ALTTA" : "USTTE"; } return "ESIT"; } console.log("yigilma sirasi (tablo basligina gore):"); const basligin = zincirler["tablo basligi (yapiskan)"]; for (const ad of ["acilir-liste (panelin icinde)", "acilir-liste (tasinmis)"]) { const z = zincirler[ad]; console.log(` ${ad.padEnd(30)} zincir=[${z.join(", ")}] -> ${karsilastir(z, basligin)}`); }
kirpma: panelin icinde gorunen 40/240 piksel (280-320) tasinmis (govde koku) gorunen 240/240 piksel (280-520) yigilma sirasi (tablo basligina gore): acilir-liste (panelin icinde) zincir=[1, 9999] -> ALTTA acilir-liste (tasinmis) zincir=[9999] -> USTTE
Birinci kısıt kırpmadır. Süzgeç paneli kendi kutusundan taşan içeriği kesiyorsa, iki yüz kırk piksellik listenin yalnız kırkı görünür. Bu bir biçem hatası değildir; panelin kendi kaydırma davranışı için gereken kesme, listeyi de keser.
İkinci kısıt yığılmadır. CSS ile Görsel Sunum kursunda tanımlanan yığılma bağlamı, bir ögenin sıra numarasını yalnız kendi bağlamı içinde anlamlı kılar. Panel kendi bağlamını kuruyorsa, içindeki dokuz bin dokuz yüz doksan dokuz değeri panelin kök bağlamdaki birine bağlıdır. Karşılaştırma zincirin ilk ögesinden yapılır: bir, beşten küçüktür, dolayısıyla liste yapışkan tablo başlığının altında kalır. Sayıyı büyütmek işe yaramaz; büyütülen sayı yanlış bağlamdadır.
Her iki kısıt da tek bir nedenden doğar: alt ağacın belge içindeki konumu. Konumu değiştirmenin yolu ya bileşen ağacını bozmaktır — açılır listeyi paneldeki mantıktan koparıp sayfa köküne taşımak — ya da taşımadır.
İki Ağacın Ayrılması
Taşıma, alt ağacın bileşen ağacındaki yerini olduğu gibi bırakır ve yalnız belge ağacındaki hedefini değiştirir. Sonuç, o noktadan itibaren birbirinden ayrılan iki ağaçtır.
// tasima.mjs — bilesen agaci ile belge agacinin ayrilmasi // acilir-liste bilesen agacinda suzgec-panelinin cocugu, belge agacinda govde-kokun cocugu. const BILESEN_UST = { "secenek-sicaklik": "acilir-liste", "acilir-liste": "suzgec-paneli", "suzgec-dugmesi": "suzgec-paneli", "suzgec-paneli": "sayfa", "baslik": "sayfa", "olcum-tablosu": "sayfa", "sayfa": null, }; const BELGE_UST = { "secenek-sicaklik": "acilir-liste", "acilir-liste": "govde-kok", "govde-kok": "belge", "suzgec-dugmesi": "suzgec-paneli", "suzgec-paneli": "uygulama-koku", "baslik": "uygulama-koku", "olcum-tablosu": "uygulama-koku", "uygulama-koku": "belge", "belge": null, }; const SAGLAYICILAR = { sayfa: "santigrat" }; // birim baglami sayfada saglaniyor const VARSAYILAN_BIRIM = "kelvin"; const yol = (harita, dugum) => { const y = []; for (let d = dugum; d; d = harita[d]) y.push(d); return y; }; const HEDEF = "secenek-sicaklik"; const bilesenYolu = yol(BILESEN_UST, HEDEF); const belgeYolu = yol(BELGE_UST, HEDEF); console.log(`hedef: ${HEDEF}`); console.log(` bilesen agaci yolu: ${bilesenYolu.join(" > ")}`); console.log(` belge agaci yolu : ${belgeYolu.join(" > ")}`); console.log("tiklama dinleyicileri:"); for (const dinleyici of ["suzgec-paneli", "olcum-tablosu", "uygulama-koku", "govde-kok"]) { const b = bilesenYolu.includes(dinleyici); const d = belgeYolu.includes(dinleyici); console.log( ` ${dinleyici.padEnd(15)} bilesen agacina gore: ${b ? "calisir " : "calismaz"}`, `| belge agacina gore: ${d ? "calisir" : "calismaz"}`, ); } console.log("baglam cozumlemesi (birim):"); for (const [ad, y] of [["bilesen agaci", bilesenYolu], ["belge agaci", belgeYolu]]) { const bulunan = y.find((d) => d in SAGLAYICILAR); console.log(` ${ad.padEnd(14)} -> ${bulunan ? SAGLAYICILAR[bulunan] : VARSAYILAN_BIRIM}`, `(${bulunan ? `saglayici: ${bulunan}` : "saglayici yok"})`); }
hedef: secenek-sicaklik bilesen agaci yolu: secenek-sicaklik > acilir-liste > suzgec-paneli > sayfa belge agaci yolu : secenek-sicaklik > acilir-liste > govde-kok > belge tiklama dinleyicileri: suzgec-paneli bilesen agacina gore: calisir | belge agacina gore: calismaz olcum-tablosu bilesen agacina gore: calismaz | belge agacina gore: calismaz uygulama-koku bilesen agacina gore: calismaz | belge agacina gore: calismaz govde-kok bilesen agacina gore: calismaz | belge agacina gore: calisir baglam cozumlemesi (birim): bilesen agaci -> santigrat (saglayici: sayfa) belge agaci -> kelvin (saglayici yok)
Bağlam çözümlemesi ve olay yayılımı için hangi ağacın geçerli olduğu, taşımanın en çok şaşırtan yanıdır. Yanıt tektir: bileşen ağacı.
Bir seçeneğe tıklandığında olay, belge ağacında yukarı çıkar — ki orada süzgeç paneli yoktur — ama çalışma zamanı olayı bileşen ağacındaki yol boyunca yeniden yayar. Süzgeç panelinde kurulmuş bir tıklama dinleyicisi çalışır. Bu, Tarayıcı ve Web Platformu kursundaki gölge ağacın birleşik yolla yaptığı işin bir benzeridir: fiziksel yayılım bir ağaçta, mantıksal yayılım başka bir ağaçtadır.
Aynı şey bağlam için de geçerlidir. Taşınan alt ağaç, belge ağacında sağlayıcının dışında kalmış olsa da bileşen ağacındaki sağlayıcıyı görür ve santigratı okur. Belge ağacı üzerinden çözümlense varsayılana düşerdi. Aynı kural önceki iki dersin mekanizmaları için de işler: taşınan alt ağaçtaki bir oluşturma hatası, bileşen ağacındaki en yakın hata sınırına gider; taşınan alt ağaçtaki bir askıya alma, bileşen ağacındaki en yakın bekleme sınırında yakalanır.
Bu tutarlılık taşımayı kullanılabilir kılan şeydir. Taşıma bir kaçış yolu değildir; yalnız çizim hedefini değiştirir, mantıksal ilişkilerin hiçbirini bozmaz.
Hedef Düğümün Koşulları
Hedefin sağlaması gereken üç koşul vardır.
Hedef, alt ağaç yazılırken belgede bulunmalıdır. Uygulamanın kökünün yanına kardeş bir kap koymak bu koşulu en basit biçimde karşılar. Hedefi program içinde üretip aynı anda kullanmak, düğümün henüz belgeye eklenmemiş olması riskini taşır.
Hedefin ömrü, taşınan bileşenin ömründen uzun olmalıdır. Hedef, taşıyan bileşenin kendisi tarafından üretiliyorsa, bileşen ağaçtan çıkarken hedefi de götürür ve söküm sırası belirsizleşir.
Hedef, uygulamanın söküm sırasına dahil olmalıdır. Taşınan alt ağaç, kendi yan etkilerinin temizlemesini normal biçimde çalıştırır; ama hedef kabın kendisi uygulama dışında üretilmişse boş bir düğüm olarak belgede kalır. Tek bir paylaşılan hedef kullanmak bu birikimi önler.
Sunucu tarafında oluşturma yapılıyorsa dördüncü bir koşul eklenir: sunucuda bir belge bulunmadığı için taşınan içerik ilk yanıtta yer almaz, ilk yazma aşamasından sonra belirir. İlk görünümde bulunması gereken içerik taşınmaz.
Erişilebilirlik
Taşıma, görsel ilişkiyi düzeltirken belgedeki ilişkiyi koparır ve bu kopukluk yardımcı teknolojiler için gerçektir. Dört yükümlülük doğar.
Okuma sırası. Belge ağacında açılır liste artık sayfanın sonundadır; ekran okuyucu onu tetikleyen düğmenin yanında değil, sonda bulur. Bağ, düğme ile listeyi ilişkilendiren öznitelikler ve uygun ARIA rolleriyle yeniden kurulur.
Odak yönetimi. Liste açıldığında odak listeye taşınır, kapandığında onu açan düğmeye geri döner. Odağın geri dönmemesi, kullanıcıyı belgenin sonunda bırakır. Web’in Temelleri ve HTML kursundaki odak yönetimi kuralları burada değişmeden geçerlidir.
Odak tuzağı. Kalıcı bir katman söz konusuysa — bir onay penceresi — odak katmanın içinde tutulur ve arkadaki içerik yardımcı teknolojilerden gizlenir. Görsel olarak örtülmüş bir içerik, gizlenmediği sürece hâlâ okunabilir durumdadır.
Kapatma yolu. Sekme sırası dışına çıkmış bir katmanın klavyeyle kapatılabilmesi gerekir; kaçış tuşu ve dışarı tıklama iki ayrı yoldur ve ikisi de kurulur.
Taşımanın Gerekmediği Durumlar
Taşıma bir maliyet taşır: iki ağacın ayrılması, hata ayıklamayı zorlaştırır ve yukarıdaki dört yükümlülüğü doğurur. Gerekmediği yerde kullanılmaz.
Kırpma sorunu çoğu zaman düzenle çözülür. Panelin taşan içeriği kesmesi gerçekten gerekli mi, yoksa bir kaydırma kabı yanlış yere mi konmuş? Yığılma sorunu da zincirin düzeltilmesiyle çözülebilir: paneli gereksiz yere yığılma bağlamı kuran bir özellikten kurtarmak, listeyi kök bağlama geri getirir.
Ölçüt tek cümleyle yazılabilir: taşıma, alt ağacın belge içindeki konumu düzeltilemediğinde kullanılır. Konum düzeltilebiliyorsa taşımaya gerek yoktur.
Özet
- Taşıma, alt ağacın bileşen ağacındaki yerini koruyup belge ağacındaki hedefini değiştirir.
- Gerekçesi iki ölçülebilir kısıttır: üst kabın taşan içeriği kırpması ve üst kabın kurduğu yığılma bağlamının sıra numarasını kendi içine hapsetmesi.
- Olay yayılımı, bağlam çözümlemesi, hata sınırları ve bekleme sınırları bileşen ağacını izler; belge ağacındaki kopukluk bu ilişkileri bozmaz.
- Hedef düğüm yazma anında belgede bulunmalı, taşınan bileşenden uzun ömürlü olmalı ve uygulamanın söküm sırasına dahil olmalıdır.
- Taşıma belgedeki okuma sırasını bozar; ilişkilendirme öznitelikleri, odağın geri dönmesi, gerekliyse odak tuzağı ve klavyeyle kapatma yolu ayrıca kurulur.
- Kırpma ve yığılma düzenle çözülebiliyorsa taşıma kullanılmaz.
Sonraki Adım
Bu konu tek bir bileşenin iç yaşamını tamamladı. Bileşenin oluşturmalar arasında ne sakladığını, dış dünyayla nasıl eşzamanlandığını, hangi değerleri hesapladığını, hangilerini sessizce tuttuğunu, ağaçtan ne okuduğunu, hata ve bekleme durumlarında ne olduğunu ve çıktısını nereye çizdiğini biliyoruz. Hepsi tek bir bileşenin sınırları içinde kaldı.
Ölçüm istasyonu sayfası ise tek bir bileşen değil; süzgeç paneli, ölçüm tablosu, ölçüm rozeti ve karşılaştırma panosundan oluşan bir aile. Bu parçaların birbirine nasıl bağlanacağı ayrı bir sorudur ve yanıtı yalnız “üstten alta özellik vermek” değildir. Bir bileşen başka bir bileşeni içerik olarak alabilir, iki bileşen ortak bir sözleşmeyi paylaşabilir, aynı davranış farklı görünümlerle yeniden kullanılabilir. Sonraki konunun ilk dersi olan Bileşim Kalıpları, bileşenleri birleştirmenin bu yollarını ve her birinin kapsülleme ile esneklik arasında kurduğu dengeyi ele alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.