स्पेशल नेविगेशन (डायरेक्शनल कीपैड फ़ोकस)

वेब की सुविधाएं: scrollIntoView()

डायरेक्शनल फ़ोकस नेविगेशन (ऐरो बटन या डी-पैड का इस्तेमाल करके) की मदद से, सिर्फ़ कीबोर्ड और अन्य इनपुट का इस्तेमाल करने वाले लोग, इंटरैक्टिव आइटम को उनके विज़ुअल 2D लेआउट के आधार पर नेविगेट कर सकते हैं. यह स्पेसियल यूज़र इंटरफ़ेस (जैसे, मीडिया रेल, डैशबोर्ड पैनल या नेस्ट किए गए कैटलॉग) के लिए काफ़ी फ़ायदेमंद है. हालांकि, इसे ध्यान से लागू किया जाना चाहिए, ताकि व्यूपोर्ट स्क्रोलिंग जैसे नेटिव ब्राउज़र के व्यवहार में कोई रुकावट न आए.

इस गाइड में, कंपोज़िट विजेट में बेहतर परफ़ॉर्मेंस वाले और ऐक्सेस किए जा सकने वाले 2D डायरेक्शनल नेविगेशन बनाने के बारे में बताया गया है.


आर्किटेक्चर के बुनियादी सिद्धांत

ऐरो बटन की मदद से, कस्टम स्पेशल फ़ोकस लागू करते समय इन बुनियादी नियमों का पालन करें:

  1. कंपोज़िट विजेट तक सीमित करें: सामान्य दस्तावेज़ के टेक्स्ट फ़्लो या सामान्य वर्टिकल लिस्ट लेआउट पर, ऐरो बटन को ओवरराइड न करें. इनमें डिफ़ॉल्ट तौर पर, क्रम से टैब नेविगेशन की सुविधा होती है. दस्तावेज़ के सामान्य फ़्लो से जुड़े दिशा-निर्देशों के लिए, सुलभता कोडिंग से जुड़े दिशा-निर्देश देखें.
  2. नेटिव बाउंड्री स्क्रोलिंग को बनाए रखें: अगर दबाई गई दिशा में फ़ोकस करने लायक कोई एलिमेंट नहीं है, तो डिफ़ॉल्ट ब्राउज़र के काम करने के तरीके को न रोकें. इवेंट को बबल होने दें, ताकि ब्राउज़र व्यूपोर्ट को नेटिव तौर पर स्क्रोल कर सके. ऐरो बटन को ओवरराइड करने से, सुलभता की सुविधा काम नहीं करती (डब्ल्यूसीएजी 1.4.10).
  3. फ़ोकस की विज़िबिलिटी और अलाइनमेंट पक्का करना: फ़ोकस बदलने पर, पक्का करें कि नया फ़ोकस किया गया एलिमेंट, व्यू में स्क्रोल हो जाए (उदाहरण के लिए, scrollIntoView({ block: 'nearest', inline: 'nearest' }) का इस्तेमाल करके), ताकि वह पूरी तरह से दिखता रहे.
  4. लेआउट सिस्टम के साथ तालमेल बिठाना: हर कीप्रेस को हैंडल करते समय, लाइव बाउंडिंग ज्यामिति (getBoundingClientRect()) से फ़ोकस कैंडिडेट की पोज़िशन तय करें. यह न मानें कि डीओएम ऑर्डर या तय ग्रिड स्ट्रक्चर, विज़ुअल लेआउट को दिखाता है. मॉडर्न लेआउट के सुझावों के लिए, सीएसएस लेआउट और रिस्पॉन्सिव डिज़ाइन देखें.

ऐक्सेस किया जा सकने वाला फ़ोकस मैनेजमेंट

स्पेशल कंटेनर में, क्रम से पेज टैब नेविगेशन के लिए एक टैब स्टॉप होना चाहिए. विजेट में, रोविंग टैबइंडेक्स पैटर्न का इस्तेमाल करके कीबोर्ड फ़ोकस को मैनेज करें:

  • चालू आइटम में tabindex="0" है.
  • सभी इनऐक्टिव आइटम में tabindex="-1" है.
  • नेविगेट करते समय, tabindex="0" एट्रिब्यूट को शिफ़्ट करके और focus() को कॉल करके, प्रोग्राम के हिसाब से फ़ोकस को मूव करें.

विज़ुअल कैंडिडेट चुनना

असिमेट्रिकल, रैप किए गए या ग्रिड लेआउट में, इंडेक्स के आधार पर मैपिंग नहीं की जा सकती. इसके बजाय, मौजूदा कीप्रेस के लिए लाइव विज़ुअल ज्यामिति से कैंडिडेट जनरेट करें:

  • कैंडिडेट: विजेट कंटेनर के फ़ोकस किए जा सकने वाले डिसेंडेंट का इस्तेमाल करें, जो checkVisibility() को पास करते हैं. छिपे हुए एलिमेंट में (0, 0) पर शून्य साइज़ का रेक्टैंगल हो सकता है. साथ ही, वे गलती से up या left टारगेट नहीं बनने चाहिए.
  • ज्यामिति: बटन दबाने पर, फ़ोकस किए गए एलिमेंट और हर कैंडिडेट के लिए getBoundingClientRect() को पढ़ें. इन रेक्ट को ग्लोबल तौर पर कैश मेमोरी में सेव न करें: लेआउट कई वजहों से बदल सकता है. साथ ही, उपयोगकर्ता की रफ़्तार से होने वाले मुख्य इवेंट को मैनेज करते समय, ज्यामिति को कम संख्या में पढ़ना सस्ता होता है.
  • डायरेक्शन फ़िल्टर: कोई कैंडिडेट, दबाई गई दिशा में सिर्फ़ तब होता है, जब उसका नज़दीकी किनारा उस ऐक्सिस पर मौजूदा एलिमेंट के दूर वाले किनारे पर या उससे आगे हो. इससे सब-पिक्सल राउंडिंग के लिए थोड़ी छूट मिलती है. उदाहरण के लिए, right के लिए उम्मीदवार के पास candidate.left >= current.right - tolerance होना चाहिए. उम्मीदवार सही दिशा में है या नहीं, यह तय करने के लिए सेंटर की तुलना न करें.
  • अलाइन किए गए कैंडिडेट पहले: डायरेक्शन फ़िल्टर पास करने वाले कैंडिडेट में से, उन कैंडिडेट को प्राथमिकता दें जिनका परपेंडिकुलर ऐक्सिस पर एक्सटेंट, मौजूदा एलिमेंट के एक्सटेंट को ओवरलैप करता है. नेविगेशन ऐक्सिस पर सबसे छोटा गैप चुनें. इसके लिए, सबसे छोटे परपेंडिकुलर सेंटर ऑफ़सेट का इस्तेमाल करें. इससे, किसी आइटम के सामान्य से ज़्यादा चौड़ा होने पर भी, बगल वाली विज़ुअल लाइन या कॉलम में नेविगेशन बना रहता है.
  • फ़ॉलबैक स्कोरिंग: अगर कोई भी कैंडिडेट, परपेंडिकुलर ऐक्सिस पर ओवरलैप नहीं होता है, तो बाकी कैंडिडेट को gap + w * perpendicularCentreOffset के तौर पर स्कोर करें और सबसे कम स्कोर चुनें. शुरुआत में, वेट w >= 2 का इस्तेमाल करें. इसके बाद, लेआउट के हिसाब से इसे ट्यून करें. ज़्यादा वेट होने पर, उन कैंडिडेट को प्राथमिकता दी जाती है जो ज़्यादा करीब से अलाइन होते हैं.
  • कोई उम्मीदवार नहीं: कोई नतीजा नहीं दिखाएं. इवेंट हैंडलर को preventDefault() को कॉल नहीं करना चाहिए, ताकि ब्राउज़र को नेटिव तौर पर स्क्रोल करने की अनुमति मिल सके.

लागू करने का तरीका अलग-अलग हो सकता है. हालांकि, डायरेक्शन टेस्ट को एज-आधारित रखें और वेटेड फ़ॉलबैक से पहले, ओवरलैप की प्राथमिकता लागू करें. यह एल्गोरिदम का मुख्य हिस्सा है. कैश मेमोरी या तय वेट को हर जगह सही के तौर पर दिखाने से बचें.


कीप्रेस इंटिग्रेशन और स्क्रोल कोऑपरेशन

आपके इवेंट राऊटर को स्टैंडर्ड व्यूपोर्ट स्क्रोलिंग को सिर्फ़ तब रोकना चाहिए, जब कोई मान्य स्पेशल फ़ोकस ट्रांज़िशन होता है:

  • कार्रवाई बदलने वाली कुंजी को दबाकर रखने पर, इवेंट को इंटरसेप्ट न करें. ऐसा तब भी न करें, जब e.defaultPrevented पहले से ही 'सही' पर सेट हो या सबसे अंदरूनी इवेंट टारगेट, ऐरो बटन का इस्तेमाल खुद करता हो. सिर्फ़ e.target के बजाय e.composedPath()[0] का इस्तेमाल करें, क्योंकि e.target को शैडो होस्ट पर फिर से टारगेट किया जा सकता है. बदलाव किए जा सकने वाले/कंट्रोल चेक का एक व्यावहारिक उदाहरण target.matches(':read-write, select, input[type="range"], [role="slider"]') है; क्लोज़्ड शैडो रूट की जांच नहीं की जा सकती.
  • लिसनर को विजेट कंटेनर तक सीमित करें, ताकि एक पेज पर कई स्पेशल विजेट मौजूद हो सकें.
  • सिर्फ़ तब कार्रवाई करें, जब फ़ोकस किया गया एलिमेंट खुद एक कैंडिडेट हो. अगर आइटम में फ़ोकस किए जा सकने वाले कंट्रोल मौजूद हैं, तो उन्हें भी कैंडिडेट बनाएं या उन्हें ऐसे ही छोड़ दें. पैरंट को closest() के साथ रिज़ॉल्व न करें.
  • जब कोई उम्मीदवार मिल जाए, तो preventDefault() को कॉल करें, रोविंग tabindex को अपडेट करें, focus() को कॉल करें, और scrollIntoView({ block: 'nearest', inline: 'nearest' }) का इस्तेमाल करें.
  • अगर कोई कैंडिडेट नहीं मिलता है, तो preventDefault() को कॉल किए बिना वापस जाएं, ताकि नेटिव स्क्रोलिंग जारी रहे.

स्पेशल नेविगेशन के लिए, एक वर्ण वाले शॉर्टकट जोड़ने से बचें. जैसे, h/j/k/l या W/A/S/D. ऐरो बटन, वर्ण बटन नहीं होते. साथ ही, कोई कैंडिडेट न होने पर नेटिव स्क्रोलिंग को बनाए रखने से, व्यूपोर्ट के मूवमेंट को बेवजह ब्लॉक होने से रोका जा सकता है.