1 पॉइंट द्वारा GN⁺ 2024-11-19 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • BBC UK वेबसाइट पर More बटन कुछ खास work-from-home सेटअप में ही क्लिक को सही तरह प्रोसेस नहीं कर रहा था; दिखने में साधारण UI bug असल में multi-monitor coordinate system की समस्या निकला
  • जब बाहरी मॉनिटर मुख्य मॉनिटर के ऊपर या बाईं ओर रखा हो, तो Chrome और Firefox के click event में screenX, screenY नकारात्मक हो सकते थे
  • मौजूदा code pointer click पहचानने के लिए event.screenX > 0 || event.screenY > 0 का उपयोग कर रहा था, इसलिए negative coordinate click को mouse click नहीं माना गया
  • fix सरल था: screenX, screenY 0 से बड़े हैं या नहीं, यह देखने के बजाय 0 नहीं हैं या नहीं, यह जांचना; यानी event.type === 'click' && (event.screenX!== 0 || event.screenY!== 0)
  • unit test, Puppeteer, manual test और assistive technology test से गुजरने के बावजूद, UI Events spec की अस्पष्टता और multi-monitor coordinate assumptions की वजह से ऐसा bug बचा रह सकता है

सिर्फ़ खास वातावरण में दोहराया गया BBC navigation bug

  • BBC UK वेबसाइट का navigation bar तब menu खोलता है जब उपयोगकर्ता More बटन को activate करता है
  • यह बटन click event का उपयोग करता है, और यह event सिर्फ़ mouse से नहीं बल्कि touch, keyboard के Enter, Space से भी हो सकता है
  • टीम के एक सदस्य को यह समस्या सिर्फ़ घर से काम करते समय अपने work laptop पर हुई, जबकि वही laptop ऑफिस में ठीक काम करता था
  • घर पर भी यह सिर्फ़ तब fail होता था जब browser window बाहरी मॉनिटर पर होती थी; laptop स्क्रीन पर बटन सामान्य रूप से काम करता था
  • समस्या होने पर JavaScript handler menu खोलने के बजाय no-JavaScript fallback व्यवहार के जरिए menu खोलता था
  • Safari में यही समस्या नहीं दिखी

दोहराव की शर्त: मॉनिटर की स्थिति

  • टीम ने घर के सेटअप में कौन-सा factor समस्या पैदा कर रहा है, यह पता करते हुए reproduction condition को संकीर्ण किया
  • बाहरी मॉनिटर laptop स्क्रीन के ऊपर रखा गया था, और OS settings में यह व्यवस्था बदलने पर समस्या रुक गई
  • दूसरे टीम सदस्य ने भी OS में मॉनिटर की वही व्यवस्था सेट की तो bug दोहराया जा सका
  • जांच की शुरुआती अवस्था में दो बातें सामने आईं
    • Safari में समस्या नहीं होती थी
    • जब बाहरी मॉनिटर मुख्य मॉनिटर के ऊपर और बाईं ओर हो, तब समस्या होती थी

screenX, screenY के नकारात्मक coordinate

  • More बटन के click event को console.log से देखने पर Chrome और Firefox में screenX, screenY की value negative आई
  • click event, चाहे किसी भी input से आया हो, PointerEvent का एक प्रकार है, इसलिए event object में click पैदा करने वाले mouse या touch pointer की जानकारी शामिल होती है
  • screenX, screenY स्क्रीन पर क्लिक हुए बिंदु के coordinate को pixel में दिखाते हैं
  • DOM UI Events spec में यह स्पष्ट जानकारी नहीं मिली कि ये properties negative हो सकती हैं या नहीं
  • Safari और Chrome/Firefox के बीच का अंतर दिखाता है कि multi-monitor configuration में browser के screen coordinates दिखाने का तरीका अलग हो सकता है
  • इस interoperability समस्या की रिपोर्ट WebKit टीम को दी गई

browser के अनुसार multi-monitor coordinate तरीका अलग

  • multi-monitor setup में browser का screen coordinate system कई मॉनिटर को एक बड़ी स्क्रीन की तरह मानता है
  • अगर 800px के 2 मॉनिटर क्षैतिज रूप से लगे हों, तो x coordinate range 0 से 1600 तक हो सकती है
  • Safari में coordinate range हमेशा सबसे ऊपर-बाएँ मॉनिटर से शुरू होने वाली positive range जैसी दिखती है
  • Chrome और Firefox में coordinates मुख्य मॉनिटर के आधार पर calculate होते दिखते हैं, इसलिए मुख्य मॉनिटर से ऊपर या बाईं ओर मौजूद स्क्रीन पर negative coordinates आ सकते हैं
  • इस bug में समस्या सिर्फ़ तब हुई जब screenX, screenY negative थे

असली समस्या वाला code और fix

  • समस्या वाले code में isInvokedByMouse, click event mouse या touch pointer से आया है या नहीं, यह जानने के लिए screenX, screenY positive हैं या नहीं, यह जांच रहा था
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...
  }
};
  • यह code मानकर चल रहा था कि pointer से आए click event के screenX, screenY positive होंगे
  • जब उपयोगकर्ता ऐसे मॉनिटर पर More बटन क्लिक करता था जिसका screen coordinate negative था, तो event handler उस click को मान्य नहीं मानता था और More link की default action पर गिर जाता था
  • fix यह था कि screenX, screenY 0 से बड़े हैं या नहीं, यह देखने के बजाय यह देखा जाए कि वे 0 नहीं हैं
const isInvokedByMouse = event =>
  event.type === 'click' && (event.screenX !== 0 || event.screenY !== 0);
  • इस बदलाव से अब असामान्य multi-monitor layout इस्तेमाल करने वाले उपयोगकर्ता भी BBC वेबसाइट का navigation bar इस्तेमाल कर सकते हैं

बाकी design issues और बाद की refactoring

  • fix अपने आप में सरल था, लेकिन code में कुछ अजीब हिस्से अब भी बचे थे
  • यह जांचने की ज़रूरत नहीं थी कि click mouse से आया या keyboard से, और event handler keydown event तक संभाल रहा था, जिससे complexity बढ़ गई थी
  • API के behavior के बारे में कौन-सी assumptions रखी जा रही हैं, इस पर सावधानी चाहिए; screenX, screenY negative हो सकते हैं या नहीं, इस बारे में spec का अस्पष्ट होना भी समस्या को छिपाने वाला कारक था
  • यह code unit test, Puppeteer test, कई browsers/devices और assistive technology tools के साथ manual testing से गुजर चुका था, फिर भी bug पकड़ा नहीं गया
  • 19 नवंबर 2024 के update के अनुसार, navigation component बाद में refactor किया गया और menu button event handler भी काफी बदल गया
  • अगला लेख refactoring के तरीके और बार-बार पूछे गए सवालों के जवाब देता है: How I refactored the BBC navigation bar and a follow-up FAQ

1 टिप्पणियां

 
GN⁺ 2024-11-19
Hacker News टिप्पणियाँ
  • जिन लोगों ने WebKit bug report तक क्लिक करके नहीं देखा, उनके लिए संदर्भ: WebKit developer ने BBC से पूछा कि keyboard से आया event detect कर पाना क्यों उपयोगी होगा, और लेखक ने जवाब दिया कि accessibility से जुड़े use case के कारण interoperability ज़रूरी है
    BBC UK website के navigation bar menu button का व्यवहार pointer से खोलने और keyboard से खोलने पर थोड़ा अलग है। click event हमेशा menu खोलता है, लेकिन pointer से खोलने पर focus menu container पर चला जाता है, और keyboard से खोलने पर menu open animation के बिना focus menu के पहले link पर चला जाता है। click event device-independent होता है, इसलिए keyboard user experience बनाते समय अच्छा है, और keyboard पर यह सिर्फ Space या Enter से ही invoke होता है। keydown इस्तेमाल करने पर खुद check करना पड़ता है कि यह Space/Enter है या नहीं
    स्रोत: https://bugs.webkit.org/show_bug.cgi?id=281430

    • दिलचस्प बात यह है कि code और WebKit bug की explanation को अगर सीधे-सादे ढंग से अंग्रेज़ी में समझें, तो वह असली code structure से मेल नहीं खाती। संबंधित code है const isInvokedByMouse = event => event.screenX > 0 || event.screenY > 0; और const isInvokedByKeyboard = event => isEnterKey(event) || isSpaceKey(event);, जो सतह पर ऐसा लगता है मानो event को mouse या keyboard, इनमें से किसी एक में classify करने की कोशिश हो रही है
      असल में चार categories बनती हैं: mouse है और keyboard नहीं, keyboard है और mouse नहीं, दोनों है, दोनों नहीं। मूल bug की तरह “दोनों नहीं” को गलत तरीके से handle किया जाता है, और “दोनों है” भी सही से काम करता है या नहीं, इस पर शक होता है। code को यह बात जानबूझकर handle करनी चाहिए कि keyboard-ness और mouse-ness अलग-अलग boolean हैं, या फिर eventSource को "keyboard", "mouse", "not sure" जैसी mutually exclusive categories return करने के लिए बनाया जाना चाहिए
    • यह bug नहीं लगता। developer की पहली गलती keyboard और mouse के लिए अलग-अलग user experience बनाने की कोशिश थी
      default behavior के साथ align करके ऐसा component design करना बेहतर है जो दोनों use cases में काम करे। accessibility में over-smart बनने की कोशिश नहीं करनी चाहिए। अंत में समाधान लगभग hack जैसा बन गया, और ऐसे तरीके अनिवार्य रूप से टूटते हैं या side effects पैदा करते हैं। accessibility context में अलग तरह से handle करने के अच्छे handles कम मिलने की वजह यही है कि यह क्षेत्र शुरुआत से ही अलग तरह से handle करने के लिए intended नहीं था
    • यह लेख मुझे confusing लगा। मेरी समझ में BBC mouse “click” और keyboard “click” के आधार पर थोड़ा अलग behavior चाहता है, और keyboard पर animation के बिना menu के पहले link पर focus देना चाहता है
      साथ ही वह केवल एक event पर bind करने की सुविधा भी चाहता है। click यह संभव बनाता है, लेकिन event mouse click से हुआ या keyboard input से, यह जानने का तरीका नहीं है, इसलिए Chrome में mouse position screenX=0, screenY=0 हो तो उसे origin click या keyboard trigger मानने वाला unstable heuristic इस्तेमाल किया जा रहा है। accessibility projects पर काम कर चुके व्यक्ति के तौर पर यह काफी खराब idea है, और PR में देखा होता तो फिर से लिखने को कहता। browsers का same behavior रखना ideal तो है, लेकिन असली समस्या यह लगती है कि keyboard से हुए click में screenX और screenY का अर्थ लगभग नहीं होता
      Ideally, MouseEvent emit करने के बजाय keyboard और mouse दोनों पर लागू होने वाला कोई ज्यादा general event होना चाहिए, जैसे "trigger", और trigger source की जानकारी देनी चाहिए। चूंकि यह अभी spec में नहीं है और अभी समाधान चाहिए, तो keydown पर भी bind करके, उसी element पर keydown के साथ click आए तो उसे keyboard input मानना कहीं ज्यादा stable और कम hacky होगा
    • मैं समझता हूँ कि लेखक को screenX और screenY की ज़रूरत क्यों है, लेकिन यह अब भी सवाल है कि screenX को renderer के अंदर की position या rendered page position जैसे layerX, layerY के बजाय actual screen coordinates क्यों return करने चाहिए
      लेखक की ज़रूरत renderer position से भी पूरी हो सकती है, और browser window की position हर visit की गई website को leak करने की ज़रूरत नहीं पड़ेगी
    • “menu खोलते समय user ने pointer से ‘click’ किया या keyboard से ‘click’ किया, इसके आधार पर focus और animation behavior थोड़ा अलग हो, यह हम नहीं चाहते” में don’t शायद typo है, जो intended meaning का उल्टा बना रहा है
  • isInvokedByMouse पहले check करता था कि screenX और screenY 0 से बड़े हैं या नहीं, बस इसे 0 न होने की check में बदलना था” वाले हिस्से पर, यह बहुत rare होगा, लेकिन अगर user सचमुच 0,0 position पर mouse click करे तो क्या होगा, यह जानने की उत्सुकता है
    मैं JS से बहुत परिचित नहीं हूँ, क्या != 0 check सच में सबसे अच्छा या इकलौता तरीका है? दोबारा पढ़ने पर लगता है कि event handler keydown भी handle करता है इसलिए complexity है और बाद में और refactoring करनी होगी, लेकिन अभी के लिए यह fix काफी है—यह वाक्य इस हिस्से को कुछ हद तक address करता है

    • screen position lookup event की nature judge करने के लिए बनाया गया heuristic लगता है। Intuitively तो मैं instanceof MouseEvent इस्तेमाल करने की सोचता, लेकिन वह भी risky या hack जैसा लगता है
      सवाल है कि ऐसे heuristic पर निर्भर क्यों हैं। शायद इसलिए कि toggleMenu कई event handlers में इस्तेमाल होता है, या codebase की अपनी कोई और वजह हो सकती है। पूरी तस्वीर जाने बिना judgement देना मुश्किल है। जवाब शायद यहाँ है: https://news.ycombinator.com/item?id=42174177
    • revised code में पहले से event.name == 'click' check किया जा रहा है। तो समझ नहीं आता कि कुछ valid click events को filter out क्यों करना है
    • ऐसा नहीं है। primary input device pointer device है या नहीं, और आगे यह high-accuracy device है या नहीं, इस पर media selection किया जा सकता है और उसी के आधार पर filtering की जा सकती है
      पहले मैंने इसका इस्तेमाल यह चुनने के लिए किया था कि कौन सा layout दिखाना है। अगर आप सिर्फ touch input सुनना चाहते हैं, तो वैसा करके event में preventDefault call कर सकते हैं ताकि browser आगे click event न बनाए। या फिर बस मेहनत कम करके click handler लिख दें
  • BBC ने accessibility में निवेश करते हुए एक अप्रिय bug खोजा, यह सराहनीय है। लेकिन industry अब तक ऐसा dropdown ठीक से क्यों नहीं बना पाई जो सभी users के लिए consistently खुले?
    क्या accessibility सच में इतनी कठिन है? क्या BBC को पहले से ये सब handle करने वाला कोई web framework या web component इस्तेमाल करना चाहिए था? Backend-केंद्रित full-stack developer होने के नाते browser components में हाथ डालना मुझे जोखिम भरा लगता है। Behaviour में बहुत सारी बारीकियां होती हैं और implementations लंबे समय से tested होती हैं। उदाहरण के लिए, custom text box बनाते समय platform-specific text box behaviour की गहराई से जांच न करना आसानी से fail हो सकता है। बड़ी companies की sites पर भी copy/paste टूटना और characters गायब होना अक्सर दिखता है। 2024 में text box क्यों टूटते हैं, समझ नहीं आता, और अब React arrogant-सा लगने लगा है
    निजी तौर पर मैं server-side templates, Bulma जैसे CSS framework, और न्यूनतम JS से काम चलाने की कोशिश करता। Smooth custom branding मांगने वाली sites के लिए यह सही नहीं है, लेकिन text boxes ठीक चलते हैं और development cost भी बहुत ज्यादा नहीं होती। यह BBC के accessibility standards को satisfy करेगा या नहीं, इसका भरोसा नहीं है

    • मुझे सभी सवालों के जवाब नहीं पता, लेकिन “क्या accessibility इतनी कठिन है” के लिए मैं निश्चित रूप से हां कह सकता हूं
      एक वास्तविक उदाहरण modal है। अगर कोई visual impairment नहीं है, तो आप gray “छूना नहीं” area के ऊपर एक white box और उसके अंदर floating UI components देख सकते हैं। Screen reader इस्तेमाल करने पर यह guarantee नहीं कि आपको वही जानकारी मिलेगी। Tab से UI elements में घूमते हुए जब आप box के top पर वापस आते हैं, तो क्या कोई specific screen reader यह बताएगा? क्या वह available interactive elements की list देगा? क्या वह उन्हें दूसरे screen reader जैसी ही order में list करेगा? Phone पर क्या होगा, Mac पर क्या होगा? क्या screen reader और browser input elements को ठीक से report करेंगे, या user को चुपचाप modal से बाहर निकलकर site के बाकी हिस्से में लौटने देंगे?
      Accessibility में आप भरोसा नहीं कर सकते कि operating system, browser और screen reader cooperate करेंगे या सही context में reasonable तरीके से behave करेंगे। 2019 में VoiceOver + Safari में negative CSS margin के कारण RTL text block को screen reader द्वारा उल्टे order में पढ़े जाने वाला bug report करना पड़ा था। Visually यह 9/10/2019 दिखता था, लेकिन screen reader में “ten slash nine slash two-thousand-and-nineteen” जैसा सुनाई देता था, और workaround के तौर पर text को aria-hidden करना पड़ा और सही order वाला invisible p tag डालना पड़ा। इसलिए accessibility से जुड़ा अजीब code दिखे तो कई बार सच में उससे बेहतर तरीका नहीं होता। Codebase को पूरी तरह पलटकर accessibility को top priority बना दें, तब भी JAWS या VoiceOver update आते ही यह समझ से बाहर तरीके से टूट सकता है
    • सहमत हूं। हालांकि कई समस्याएं आखिरकार तब पैदा होती हैं जब user agents इन elements को बहुत संदिग्ध तरीकों से customize करते हैं
      आम तौर पर सब ठीक रहता है, लेकिन reset.css files मौजूद होने की वजह है, और यहां लगता है कि ऐसी problems को पूरी तरह bypass करने के लिए शायद और extreme approach अपनाया गया। मैं उनके decision का अनुमान लगाने की कोशिश कर रहा हूं
  • यह गलत heuristic से पैदा हुआ खुद बुलाया हुआ bug लगता है। Positive screenX/Y values को mouse event मान लिया गया, और tracking/logging की कमी ने investigation को और complicated बना दिया
    दूसरे comments ने जो ज्यादा appropriate property pointerType check करने का सुझाव दिया, उसके बजाय author का solution डगमगाते heuristic पर और patches चढ़ाना है, यह थोड़ा हैरान करता है। जैसे अंतिम दो clues से यह conclusion निकाला गया कि screenX और screenY coordinates check करते समय सिर्फ positive नहीं, negative भी check करने चाहिए

    • असल में ऐसा ही करने वाले हैं। जल्द ही code merge करेंगे जिसमें pointerId === -1 इस्तेमाल होगा, और फिर screenX === 0 पर fallback होगा
      करीब 4 साल पहले जब यह code पहली बार लिखा गया था, तब सभी browsers click के लिए PointerEvent इस्तेमाल नहीं करते थे
  • मुझे समझ नहीं आता कि website को शुरू से ही screen coordinate system में mouse position क्यों मिल सकती है

    • मैंने वजह ढूंढी लेकिन खास कुछ नहीं मिला। Website browser window position यानी window.screenX/window.screenY जान सकती है और click position भी उसी coordinate system में report हो सकती है—desktop पर यह बेतुका लगता है
      TOR Browser fingerprinting से बचने के लिए शायद screenX और screenY को mask करता है। सोच रहा हूं कि किसी ने इस feature का अच्छा use case देखा है क्या। मन में बस interacting dual-window applications, या virtual screen में position के आधार पर अलग behave करने वाली sites आती हैं
    • छोटे-छोटे interacting browser windows से graphics बनाने वाला game बनाते समय यह useful है
      उदाहरण: https://youtu.be/3al8prbfK5o?si=loNtyqIfMFkppm5V
    • क्योंकि 1995 में JavaScript develop करने के लिए मिले 10 दिनों में इसे implement करना आसान था, और उसके बाद backward compatibility चलती रही :(
    • अगर आप click event पर react कर रहे हैं, तो clicked position के coordinates जानना चाह सकते हैं। मुख्यतः click-drag operations में events के बीच delta निकालकर dragged object की position update करने में इस्तेमाल होता है
      event.type check करने के बजाय coordinates क्यों check किए जा रहे हैं, यह समझ नहीं आता। फिर भी लेख अपने आप में अच्छा puzzle है, और किसी और के लिखे code को देखते हुए “click coordinates का 0 न होना क्यों important है?”, “बस event.target check नहीं कर सकते कि वही button activate करना है?”, “details/summary tags से वही काम हो सकता है तो JavaScript क्यों?” जैसे सवाल पूछने वाली situation relatable है
    • JavaScript-free CAPTCHA में इस्तेमाल होता है। अच्छा काम करता है, और click करते समय सिर्फ mouse click के x और y भेजता है
  • शुरुआत में ही screen coordinates से filter क्यों किया जा रहा है? अगर user बिना screen वाले alternative input device का इस्तेमाल कर रहा हो तो क्या होगा?
    सिर्फ click event ही यह बताने के लिए काफी signal है कि user menu activate करना चाहता था। Wheel को फिर से invent क्यों किया जा रहा है, समझ नहीं आता

    • लेख के अनुसार isInvokedByMouse यह जांचने के लिए screenX या screenY coordinates positive हैं या नहीं देखता था कि click event keyboard से नहीं, बल्कि mouse या touch pointer से invoke हुआ है
      यानी keyboard activation और mouse activation detect करने की कोशिश थी, और author ने मान लिया था कि mouse event के screen coordinates हमेशा positive होंगे
  • लोगों की जिज्ञासा वाला context समझाने और सवालों के जवाब देने के लिए एक और blog post डाली है। इसमें बताया है कि मैंने शुरुआत में ही screenX === 0 क्यों check किया था, keyboard और mouse input के हिसाब से अलग behavior क्यों चाहिए था, और आगे की गड़बड़ियों को रोकने के लिए कैसे refactor किया
    उम्मीद है मदद मिलेगी: https://www.joshtumath.uk/posts/2024-11-18-how-i-refactored-...

  • यह पता करने का सही तरीका क्या है कि यह mouse click है या keyboard click? मुझे लगता है कि सबसे हाल में हुई event के आधार पर module-level flag set करना चाहेंगे: अगर mousedown ज्यादा recent है तो isKeyboard=false, isMouse=true, और अगर keydown ज्यादा recent है तो इसका उल्टा
    तब isInvokedByMouse और isInvokedByKeyboard functions की जरूरत नहीं रहेगी। क्या कोई बेहतर तरीका है? इसके लिए screen coordinates पर निर्भर रहना बहुत संदिग्ध और hack जैसा लगता है

  • बहुत दिलचस्प है, लेकिन समझ नहीं आता कि browser monitor के हिसाब से अलग coordinates क्यों report करता है। मुझे लगा था browser webpage को ऐसा treat करता है जैसे वह full screen हो, चाहे वह किसी भी display पर हो
    Web API के पास ऐसी जानकारी होने की कोई वजह है क्या? यह security, information leakage और tracking risk जैसा लगता है

  • क्या यह development competency की समस्या नहीं है? screen coordinates नहीं, viewport coordinates इस्तेमाल करने चाहिए थे, और उन्हें .clientX और .clientY से पढ़ना चाहिए था। screen space में negative value आना bug क्यों है, यह समझ नहीं आता
    https://developer.mozilla.org/en-US/docs/Web/CSS/CSSOM_view/...