1 puan yazan GN⁺ 2024-11-19 | 1 yorum | WhatsApp'ta paylaş
  • BBC UK web sitesindeki More düğmesi, yalnızca belirli bir evden çalışma ortamında tıklamaları işleyemiyordu; sıradan görünen UI hatası aslında çoklu monitör koordinat sistemi sorunuydu
  • Harici monitör ana monitörün üstüne ve soluna yerleştirildiğinde, Chrome ve Firefox’ta click olayındaki screenX, screenY negatif olabiliyordu
  • Mevcut kod, işaretçi tıklamasını event.screenX > 0 || event.screenY > 0 ile ayırt ediyordu; bu yüzden negatif koordinatlı tıklamaları fare tıklaması olarak görmüyordu
  • Düzeltme, screenX, screenY değerlerinin 0’dan büyük olup olmadığına değil, 0 olup olmadığına bakacak kadar basitti; biçimi event.type === 'click' && (event.screenX!== 0 || event.screenY!== 0) oldu
  • Birim testleri, Puppeteer, manuel testler ve yardımcı teknoloji testlerinden geçmiş olsa bile, UI Events spesifikasyonundaki belirsizlik ve çoklu monitör koordinat varsayımları nedeniyle böyle hatalar kalabiliyor

Yalnızca belirli bir ortamda yeniden üretilebilen BBC navigasyon hatası

  • BBC UK web sitesindeki navigasyon çubuğu, kullanıcı More düğmesini etkinleştirdiğinde menüyü açan bir davranış gerçekleştiriyor
  • Bu düğme click olayını kullanıyor; bu olay yalnızca fareyle değil, dokunma ve klavyedeki Enter, Space ile de tetiklenebiliyor
  • Ekipten biri sorunu yalnızca iş dizüstü bilgisayarını evde kullanırken yaşıyor, aynı dizüstü bilgisayarı ofiste kullandığında her şey normal çalışıyordu
  • Evde de yalnızca tarayıcı penceresi harici monitördeyken başarısız oluyor, dizüstü ekranında düğme normal çalışıyordu
  • Sorun oluştuğunda JavaScript işleyicisi menüyü açmak yerine, menü JavaScript olmayan fallback davranışıyla açılıyordu
  • Safari’de aynı sorun görülmüyordu

Yeniden üretme koşulu monitör konumuydu

  • Ekip, ev ortamında hangi unsurun soruna yol açtığını kontrol ederek yeniden üretme koşulunu daralttı
  • Harici monitör dizüstü ekranının üstüne yerleştirilmişti; OS ayarlarında bu yerleşim değiştirildiğinde sorun durdu
  • Başka bir ekip üyesi de OS’te monitör yerleşimini aynı şekilde ayarlayınca hatayı yeniden üretebildi
  • Araştırmanın başında doğrulanan koşullar ikiydi
    • Safari’de sorun oluşmuyor
    • Harici monitör ana monitörün üstünde ve solunda olduğunda sorun oluşuyor

screenX, screenY için negatif koordinatlar

  • More düğmesinin click olayı console.log ile kontrol edildiğinde, Chrome ve Firefox’ta screenX, screenY değerleri negatif çıkıyordu
  • click olayı hangi girdiyle tetiklenmiş olursa olsun bir PointerEvent türü olduğundan, olay nesnesi tıklamayı oluşturan fare ya da dokunma işaretçisinin bilgilerini içeriyor
  • screenX, screenY, ekranda tıklanan noktanın koordinatlarını piksel cinsinden gösteriyor
  • DOM UI Events spec içinde bu özelliklerin negatif olup olamayacağına dair somut bir bilgi görünmüyordu
  • Safari ile Chrome·Firefox arasındaki fark, çoklu monitör yapılandırmalarında tarayıcıların ekran koordinatlarını ifade etme biçimlerinin farklı olabileceğini gösteriyor
  • Bu birlikte çalışabilirlik sorunu WebKit ekibine bildirildi

Tarayıcılara göre çoklu monitör koordinat yaklaşımı farkı

  • Çoklu monitör yapılandırmalarında tarayıcının ekran koordinat sistemi, birden çok monitörü tek bir büyük ekranmış gibi ele alıyor
  • Yatay yerleştirilmiş iki adet 800px monitör varsa x koordinat aralığı 0’dan 1600’e kadar olabilir
  • Safari’de koordinat aralığı her zaman en sol üstteki monitörden başlayan pozitif bir aralık gibi görünüyor
  • Chrome ve Firefox’ta koordinatlar ana monitör temel alınarak hesaplanıyor gibi görünüyor; ana monitörün üstünde ya da solunda kalan ekranlarda negatif koordinatlar çıkabiliyor
  • Bu hata yalnızca screenX, screenY negatif olduğunda oluşuyordu

Asıl sorunlu kod ve düzeltme

  • Sorunlu koddaki isInvokedByMouse, click olayının fare veya dokunma işaretçisiyle tetiklenip tetiklenmediğini anlamak için screenX, screenY değerlerinin pozitif olup olmadığını kontrol ediyordu
const isInvokedByMouse = event => event.screenX > 0 || event.screenY > 0;
const isInvokedByKeyboard = event => isEnterKey(event) || isSpaceKey(event);

// ...

const toggleMenu = event => {
  // ...

  if (isInvokedByMouse(event) || isInvokedByKeyboard(event)) {
    event.preventDefault();

    // Do stuff to open the menu and move the focus...
  }
};
  • Bu kod, işaretçiyle tetiklenen bir click olayında screenX, screenY değerlerinin pozitif olacağını varsayıyordu
  • Kullanıcı negatif ekran koordinatlarına sahip bir monitörde More düğmesine tıkladığında olay işleyicisi tıklamayı kabul etmiyor ve More bağlantısının varsayılan davranışına düşüyordu
  • Düzeltme, screenX, screenY değerlerinin 0’dan büyük olup olmadığına bakmak yerine 0 olup olmadığını kontrol etmekti
const isInvokedByMouse = event =>
  event.type === 'click' && (event.screenX !== 0 || event.screenY !== 0);
  • Bu değişiklikle, alışılmadık çoklu monitör yerleşimleri kullanan kullanıcılar da BBC web sitesinin navigasyon çubuğunu kullanabilir hale geldi

Kalan tasarım sorunu ve sonraki refaktör

  • Düzeltmenin kendisi basitti, ancak kodda hâlâ tuhaf kısımlar kalmıştı
  • click olayının fareyle mi klavyeyle mi tetiklendiğini kontrol etmeye gerek yok; olay işleyicisi keydown olayını da ele aldığı için karmaşıklaşmış durumda
  • API davranışı hakkında hangi varsayımlarda bulunulduğuna dikkat edilmeli; screenX, screenY için negatif değerlerin çıkıp çıkamayacağı konusunda spesifikasyonun net olmaması da sorunu gizledi
  • Bu kod birim testlerinden, Puppeteer testlerinden, farklı tarayıcılar·cihazlar·yardımcı teknoloji araçlarıyla yapılan manuel testlerden geçmişti; buna rağmen hata bulunmamıştı
  • 19 Kasım 2024 tarihli düzeltmeyle birlikte navigasyon bileşeni daha sonra refaktör edildi ve menu düğmesinin olay işleyicisi de büyük ölçüde değişti
  • Devam yazısı, refaktör yöntemini ve sık sorulan soruları yanıtlıyor: How I refactored the BBC navigation bar and a follow-up FAQ

1 yorum

 
GN⁺ 2024-11-19
Hacker News yorumları
  • WebKit hata raporuna kadar tıklamamış olanlar için ek bilgi: Bir WebKit geliştiricisi BBC'ye bir olayın klavyeden gelip gelmediğini algılayabilmenin neden yararlı olduğunu sordu; yazar da erişilebilirlikle ilgili kullanım senaryoları nedeniyle birlikte çalışabilirliğe ihtiyaç olduğunu söyledi.
    BBC Birleşik Krallık web sitesindeki gezinme çubuğu menü düğmesi, işaretçiyle açıldığında ve klavyeyle açıldığında biraz farklı davranıyor. Tıklama olayı menüyü her zaman açıyor; ancak işaretçiyle açıldığında odak menü kapsayıcısına taşınıyor, klavyeyle açıldığında ise menü açılış animasyonu olmadan menüdeki ilk bağlantıya odak taşınıyor. click olayı cihazdan bağımsız olduğu için klavye kullanıcı deneyimi oluştururken iyi; klavyede de yalnızca Space veya Enter ile çağrılıyor. keydown kullanılırsa bunun Space/Enter olup olmadığını bizzat kontrol etmek gerekiyor.
    Kaynak: https://bugs.webkit.org/show_bug.cgi?id=281430

    • İlginç olan, kodu ve WebKit hatasındaki açıklamayı yüzeysel biçimde İngilizce yorumlayınca bunun gerçek kod yapısıyla uyuşmaması. İlgili kod const isInvokedByMouse = event => event.screenX > 0 || event.screenY > 0; ve const isInvokedByKeyboard = event => isEnterKey(event) || isSpaceKey(event);; ilk bakışta olayı fare veya klavye olmak üzere iki sınıftan birine ayırmaya çalışıyormuş gibi görünüyor.
      Gerçekte ise dört kategori ortaya çıkıyor: fare ama klavye değil, klavye ama fare değil, ikisi birden, hiçbiri. Özgün hatada olduğu gibi “hiçbiri” uygunsuz biçimde ele alınıyor; “ikisi birden” durumunun da düzgün çalışıp çalışmadığı şüpheli. Kodun, klavye olup olmama ile fare olup olmamanın ayrı Boolean değerleri olduğunu bilinçli şekilde ele alması ya da eventSourceun "keyboard", "mouse", "not sure" gibi birbirini dışlayan kategoriler döndürecek şekilde kurgulanması gerekir.
    • Bu bir hata gibi gelmiyor. Geliştiricinin ilk hatası, klavye ve fare için farklı kullanıcı deneyimleri oluşturmaya çalışmasıydı.
      Varsayılan davranışa uymak ve iki kullanım senaryosunda da çalışan bir bileşen tasarlamak daha iyi. Erişilebilirlikte fazla akıllı davranmaya çalışmamak gerekir. Sonuçta çözüm hack'e yakın bir şeye dönüşmüş ve bu tür yöntemler kaçınılmaz olarak kırılır ya da yan etki üretir. Erişilebilirlik bağlamında farklı davranmak için iyi tutamakların nadir olmasının nedeni, en başta buranın farklı davranışlar için tasarlanmış bir alan olmamasıdır.
    • Bu yazı kafa karıştırıcı geliyor. Anladığım kadarıyla BBC, bunun fare “tıklaması” mı klavye “tıklaması” mı olduğuna göre biraz farklı davranış istiyor ve klavyede menünün ilk bağlantısına animasyon olmadan odak vermeye çalışıyor.
      Aynı zamanda yalnızca tek bir olaya bağlanmanın kolaylığını da istiyorlar. click bunu mümkün kılıyor; ancak olayın fare tıklamasıyla mı yoksa klavye girdisiyle mi oluştuğunu bilmenin yolu olmadığı için Chrome'da fare konumu screenX=0, screenY=0 ise bunun başlangıç noktasına tıklama veya klavye tetiklemesi olduğunu varsayan kırılgan bir sezgisel yöntem kullanıyorlar. Erişilebilirlik projeleri yapmış biri olarak bu oldukça kötü bir fikir; bir PR'da görsem yeniden yazılmasını isterdim. Tarayıcıların aynı davranması elbette ideal olurdu, ama asıl sorun klavyeyle oluşan click olayında screenX ve screenY değerlerinin neredeyse anlamsız olması gibi görünüyor.
      İdeal olarak MouseEvent yaymak yerine hem klavye hem fare için geçerli daha genel bir olay, örneğin "trigger" gibi bir şey olmalı ve tetikleme kaynağı bilgisini sağlamalı. Bu şu an spesifikasyonda olmadığı için acil bir çözüm gerekiyorsa, keydowna da bağlanıp aynı öğede keydown ile birlikte click oluşursa bunu klavye girdisi saymak çok daha sağlam ve daha az hack'vari olur.
    • Yazarın screenX ve screenYye ihtiyaç duymasını anlıyorum, ama screenXin neden render içi konum ya da render edilmiş sayfa konumu olan layerX, layerY yerine gerçek ekran koordinatlarını döndürmesi gerektiği hâlâ soru işareti.
      Yazarın ihtiyacı render konumuyla da karşılanabilir; ayrıca ziyaret edilen her web sitesine tarayıcı penceresinin konumunu sızdırmaya gerek kalmaz.
    • “Menüyü açarken kullanıcının işaretçiyle mi ‘tıklamış’ yoksa klavyeyle mi ‘tıklamış’ olduğuna bağlı olarak odak ve animasyon davranışının biraz farklı olmasını istemiyoruz” cümlesindeki don’t, niyetin tersine anlam oluşturan bir yazım hatası değil mi diye düşünüyorum.
  • isInvokedByMouseın screenX ve screenYnin 0'dan büyük olup olmadığını kontrol etmesini, yalnızca 0'a eşit olup olmadığını kontrol edecek şekilde değiştirmek yeterliydi” kısmında, çok nadir de olsa kullanıcı gerçekten 0,0 konumunda fareyle tıklarsa ne olacağını merak ediyorum.
    JS'ye aşina değilim; != 0 kontrolü gerçekten en iyi ya da tek yol mu? Tekrar okuyunca, olay işleyicisinin keydownı da işlediği için karmaşık olduğu ve ileride daha fazla refactor gerektiği, ama şimdilik bu düzeltmenin yeterli olduğu cümlesi bu noktayı bir ölçüde ele alıyor gibi.

    • Ekran konumunu sorgulamak, olayın niteliğini anlamak için oluşturulmuş bir sezgisel yöntem gibi görünüyor. Sezgisel olarak instanceof MouseEvent kullanılacağını düşünürdüm, ama bu da riskli ya da hack gibi hissettiriyor.
      Neden böyle bir sezgisel yönteme dayandıkları merak konusu. toggleMenunun birden fazla olay işleyicisinde kullanılması nedeniyle olabilir ya da kod tabanına özgü başka durumlar olabilir. Resmin tamamını bilmeden yargılamak zor. Yanıt burada gibi: https://news.ycombinator.com/item?id=42174177
    • Düzeltilmiş kodda zaten event.name == 'click' kontrol ediliyor. Öyleyse neden bazı normal tıklama olaylarını elemek istediklerini anlamıyorum.
    • Öyle değil. Birincil giriş cihazının bir işaretçi cihazı olup olmadığı, hatta daha da ileri giderek yüksek hassasiyetli bir cihaz olup olmadığı konusunda medya seçimi yapabilir ve buna göre filtreleyebilirsiniz.
      Bunu daha önce hangi düzenin gösterileceğini seçmek için kullanmıştım. Yalnızca dokunma girdisini dinlemek istiyorsanız bunu yapıp ardından olayda preventDefault çağırarak tarayıcının devamında click olayı oluşturmasını engelleyebilirsiniz. Ya da zahmete girmeyip sadece bir tıklama işleyicisi yazabilirsiniz.
  • BBC’nin erişilebilirliğe yatırım yaparken can sıkıcı bir hata keşfetmiş olması takdire değer. Ama sektör neden hâlâ tüm kullanıcılar için tutarlı biçimde açılan bir dropdown’u doğru düzgün yapamıyor?
    Erişilebilirlik gerçekten bu kadar zor mu? BBC’nin zaten bu tür şeyleri halleden bir web framework’ü ya da web component kullanması mı gerekirdi? Backend ağırlıklı bir full-stack geliştirici olarak tarayıcı component’lerine dokunurken temkinliyim. Davranışta çok ince noktalar var ve implementasyonlar uzun süredir sınanmış durumda. Örneğin özel bir metin kutusu yaparken platforma özgü metin kutusu davranışlarını derinlemesine araştırmamak kolayca başarısızlığa götürecek gibi görünüyor. Büyük şirket sitelerinde bile kopyala/yapıştırın bozulduğunu ve karakterlerin eksildiğini sık sık görüyorum. 2024’te metin kutusunun neden bozulduğunu anlamıyorum; React de artık kibirli hissettiriyor.
    Kişisel olarak bunu sunucu tarafı template’ler, Bulma gibi bir CSS framework’ü ve minimum JS ile çözmeye çalışırdım. Pürüzsüz özel branding isteyen siteler için uygun değil, ama metin kutuları düzgün çalışıyor ve geliştirme maliyeti de aşırı değil. BBC’nin erişilebilirlik standartlarını karşılayıp karşılamadığından emin değilim.

    • Tüm soruların cevabını bilmiyorum ama “erişilebilirlik gerçekten bu kadar zor mu” sorusuna kesinlikle evet diyebilirim.
      Somut örnek olarak modal var. Görme engeliniz yoksa gri bir “dokunma” alanının üzerinde beyaz bir kutu ve onun içinde yüzen UI component’leri görürsünüz. Screen reader kullanıyorsanız bu bilgiyi alacağınızın garantisi yoktur. Tab ile UI öğeleri arasında gezinip kutunun en üstüne geri geldiğinizde belirli bir screen reader bunu haber verecek mi? Kullanılabilir etkileşim öğelerini listeleyecek mi? Başka bir screen reader ile aynı sırada mı listeleyecek? Telefonda, Mac’te ne olacak? Screen reader ve tarayıcı input öğesini doğru raporlayacak mı, yoksa kullanıcının modaldan çıkıp sitenin geri kalanına dönmesine sessizce izin mi verecek?
      Erişilebilirlikte işletim sistemi, tarayıcı ve screen reader’ın işbirliği yapacağına ya da uygun durumda makul davranacağına güvenemezsiniz. 2019’da VoiceOver + Safari’de negatif CSS margin yüzünden RTL metin bloğunu screen reader’ın ters sırada okuduğu bir hatayı bildirmem gerekmişti. Görsel olarak 9/10/2019 görünüyordu ama screen reader’da “ten slash nine slash two-thousand-and-nineteen” gibi duyuluyordu; geçici çözüm olarak metni aria-hidden yapıp doğru sırada görünmez bir p etiketi eklemek zorunda kalmıştık. Bu yüzden erişilebilirlikle ilgili tuhaf kod gördüğünüzde bazen gerçekten daha iyi bir yol olmayabiliyor. Codebase’i tamamen tersyüz edip erişilebilirliği en öncelikli hale getirseniz bile JAWS ya da VoiceOver güncellendiği anda anlaşılması zor şekilde bozulabilir.
    • Katılıyorum. Ancak birçok sorun eninde sonunda user agent’ların bu öğeleri çok şüpheli biçimlerde özelleştirmesinden çıkıyor.
      Genel olarak fena değil, ama reset.css dosyalarının var olmasının bir nedeni var; burada da bu tür sorunları tamamen baypas etmek için daha uç bir yaklaşım kullanmış olmaları muhtemel görünüyor. Kararlarını anlamaya çalışıyorum.
  • Bu, hatalı bir sezgisel kuraldan kaynaklanan, kendi kendine yaratılmış bir bug gibi görünüyor. Pozitif screenX/Y değerleri varsa bunun fare olayı olduğunu varsaymışlar; izleme/loglama eksikliği de incelemeyi daha karmaşık hale getirmiş.
    Diğer yorumların önerdiği daha uygun özellik olan pointerType’ı kontrol etmek yerine, yazarın çözümünün sallantılı sezgisel kurala daha fazla yama eklemek olması biraz şaşırtıcı. Sanki son iki ipucundan, screenX ve screenY koordinatlarını kontrol ederken yalnızca pozitifleri değil negatifleri de kontrol etmek gerektiği sonucuna varılmış gibi.

    • Aslında bunu yapmayı planlıyoruz. Yakında pointerId === -1 kullanıp ardından screenX === 0’a fallback yapan kodu merge edeceğim.
      Bu kodun ilk yazıldığı yaklaşık 4 yıl önce tüm tarayıcılar click için PointerEvent kullanmıyordu.
  • En başta bir web sitesinin neden ekran koordinat sistemindeki fare konumunu elde edebildiğini anlamıyorum.

    • Nedenini araştırdım ama pek bir şey bulamadım. Bir web sitesinin tarayıcı penceresinin konumu olan window.screenX/window.screenY’yi bilebilmesi ve tıklama konumunun da bu koordinat sisteminde raporlanabilmesi masaüstünde mantıksız geliyor.
      TOR Browser parmak izi takibini önlemek için screenX ve screenY değerlerini gizliyor gibi görünüyor. Bu özelliğin iyi bir kullanım örneğini görüp görmediğinizi merak ediyorum. Aklıma birbiriyle etkileşen çift pencereli uygulamalar ya da davranışı sanal ekrandaki konuma göre değişen siteler geliyor.
    • Birbiriyle etkileşen küçük tarayıcı pencerelerinden grafik oluşturan bir oyun yaparken işe yarar.
      Örnek: https://youtu.be/3al8prbfK5o?si=loNtyqIfMFkppm5V
    • 1995’te JavaScript geliştirmek için ayrılan 10 gün içinde implement etmesi kolaydı ve sonrasında geriye dönük uyumluluk devreye girdiği için :(
    • Bir tıklama event’ine tepki veriyorsanız tıklanan konumun koordinatlarını bilmek isteyebilirsiniz. Genelde tıkla-sürükle işlemlerinde event’ler arasındaki deltayı bulup sürüklenen nesnenin konumunu güncellemek için kullanılır.
      Neden event.type’ı kontrol etmek yerine koordinatları kontrol ettiklerini anlamıyorum. Yine de yazının kendisi iyi bir bulmaca; benim yazmadığım koda bakıp “tıklama koordinatının 0 olmaması neden önemli?”, “sadece event.target’ın etkinleştirmek istediğin buton olup olmadığını kontrol edemez misin?”, “details/summary etiketleriyle aynı işi yapabiliyorken neden JavaScript kullanıyorsun?” diye sormak zorunda kalma hâli tanıdık geliyor.
    • JavaScript’siz CAPTCHA için kullanılır. İyi çalışır; tıklama sırasında yalnızca fare tıklamasının x ve y değerlerini gönderir.
  • En başta neden ekran koordinatlarına göre filtreleme yapılıyor? Kullanıcı ekransız alternatif bir giriş cihazı kullanıyorsa ne olacak?
    click event’i tek başına kullanıcının menüyü etkinleştirmek istediğine dair yeterli sinyaldir. Neden tekerleği yeniden icat ettiklerini anlamıyorum.

    • Metne göre isInvokedByMouse, click event’inin klavye yerine fare veya dokunmatik pointer tarafından tetiklenip tetiklenmediğini anlamak için screenX ya da screenY koordinatının pozitif olup olmadığını kontrol ediyordu.
      Klavye aktivasyonu mu fare aktivasyonu mu olduğunu tespit etmeye çalışmışlar; yazar da fare event’inin ekran koordinatlarının her zaman pozitif olacağını varsaymış.
  • İnsanların merak ettiği bağlamı açıklamak ve soruları yanıtlamak için bir blog yazısı daha yayımladım. Neden en başta screenX === 0 kontrolünü yaptığımı, neden klavye ve fare girdisine göre farklı davranış istediğimi ve ek kazaları önlemek için bunu nasıl refactor ettiğimi anlattım.
    Umarım faydalı olur: https://www.joshtumath.uk/posts/2024-11-18-how-i-refactored-...

  • Bunun fare tıklaması mı klavye tıklaması mı olduğunu anlamanın doğru yolu nedir? En son gerçekleşen olaya göre modül düzeyinde bir bayrak ayarlamak; mousedown daha yeniyse isKeyboard=false, isMouse=true, keydown daha yeniyse tersini yapmak isteyesim geliyor.
    Böylece isInvokedByMouse ve isInvokedByKeyboard fonksiyonlarına gerek kalmaz. Daha iyi bir yöntem var mı? Bunu ekran koordinatlarına bağlı yapmak bana çok şüpheli ve hack gibi geliyor

  • Çok ilginç, ama tarayıcının neden monitöre göre farklı koordinatlar raporladığını bilmiyorum. Tarayıcının, web sayfası hangi ekranda olursa olsun onu tüm ekran gibi ele aldığını sanıyordum.
    Web API’nin bu tür bilgilere sahip olması için bir neden var mı? Bu güvenlik, bilgi sızıntısı ve izleme riski gibi görünüyor

  • Bu bir geliştirici yetkinliği sorunu değil mi? Ekran koordinatları değil, viewport koordinatları kullanılmalıydı ve .clientX ile .clientY üzerinden okunmalıydı. Ekran uzayında negatif değerlerin çıkmasının neden bug olduğunu anlamıyorum.
    https://developer.mozilla.org/en-US/docs/Web/CSS/CSSOM_view/...