پیمایش فضایی (تمرکز صفحه‌کلید جهتی)

ویژگی‌های وب: scrollIntoView()

پیمایش تمرکز جهتی (بااستفاده از کلیدهای جهت‌نما یا پد کنترل) به کاربران ورودی جایگزین و فقط صفحه‌کلید امکان می‌دهد تا براساس چیدمان دوبعدی دیداری‌شان در موارد تعاملی پیمایش کنند. این ویژگی برای میاناهای کاربر فضایی (مانند ریل‌های رسانه، پانل‌های داشبورد، یا کاتالوگ‌های تودرتو) بسیار مفید است، اما باید با دقت پیاده‌سازی شود تا از اختلال در رفتارهای بومی مرورگر، مانند پیمایش نمای هدف، جلوگیری شود.

این راهنما ساختن پیمایش جهت‌دار دوبعدی دسترس‌پذیر و با عملکرد خوب را در ابزاره‌های ترکیبی پوشش می‌دهد.


اصول معماری اصلی

هنگام پیاده‌سازی تمرکز فضایی سفارشی با کلیدهای جهت‌نما، این قوانین پایه‌ای را رعایت کنید:

  1. محدود به ابزاره‌های ترکیبی: کلیدهای جهت‌نما را در جریان نوشتار سند عادی یا چیدمان‌های فهرست عمودی ساده که در آن‌ها پیمایش متوالی پیش‌فرض با کلید «جهش» انتظار می‌رود لغو نکنید. برای راهنمایی درباره جریان کلی سند، به دستورالعمل‌های کدنویسی دسترس‌پذیری مراجعه کنید.
  2. حفظ پیمایش مرز بومی: اگر عنصر قابل‌تمرکزی در جهت فشار داده‌شده وجود ندارد، از رفتار پیش‌فرض مرورگر جلوگیری نکنید. اجازه دهید رویداد حباب‌سازی کند تا مرورگر بتواند نمای دید را به‌طور بومی پیمایش کند. لغو کلیدهای جهت‌نما بدون قیدوشرط دسترس‌پذیری را نقض می‌کند (WCAG 1.4.10).
  3. اطمینان از نمایان بودن و تراز بودن تمرکز: وقتی تمرکز تغییر می‌کند، مطمئن شوید که عنصر جدیدی که تمرکز روی آن قرار گرفته است به نمای دید پیمایش می‌شود (مثلاً بااستفاده از scrollIntoView({ block: 'nearest', inline: 'nearest' })) تا کاملاً نمایان بماند.
  4. هماهنگ کردن با سیستم چیدمان: موقعیت‌های نامزد تمرکز را از هندسه‌های مرزهای زنده (getBoundingClientRect()) هنگام رسیدگی به هر فشار کلید تعیین کنید. فرض نکنید که ترتیب DOM یا ساختار شبکه‌ای ثابت نشان‌دهنده چیدمان دیداری است. برای توصیه‌های چیدمان مدرن، به چیدمان‌های CSS و طراحی واکنش‌گرا مراجعه کنید.

مدیریت تمرکز دسترس‌پذیر

محفظه فضایی باید یک ایستگاه پرش را برای پیمایش متوالی «زبانه صفحه» نشان دهد. در داخل ابزاره، تمرکز صفحه‌کلید را بااستفاده از الگوی شاخص زبانه گردشی مدیریت کنید:

  • مورد فعال tabindex="0" دارد.
  • همه موارد غیرفعال tabindex="-1" دارند.
  • هنگام پیمایش، با تغییر دادن ویژگی tabindex="0" و فراخوانی focus()، تمرکز را به‌صورت برنامه‌نویسی‌شده جابه‌جا کنید.

انتخاب نامزد تصویری

در چیدمان‌های نامتقارن، پیچیده، یا جدولی، نقشه‌نگاری مبتنی بر نمایه ناموفق است. درعوض، گزینه‌ها را از هندسه بصری زنده برای کلید فشرده‌شده فعلی استخراج کنید:

  • نامزدها: از فرزندان قابل‌تمرکز محفظه ابزارک که checkVisibility() را رد می‌کنند استفاده کنید. عناصر پنهان می‌توانند مستطیلی با اندازه صفر در (0, 0) داشته باشند و نباید به‌طور تصادفی به هدف up یا left تبدیل شوند.
  • هندسه: getBoundingClientRect() را برای عنصر کانونی‌شده و هر نامزد هنگام رسیدگی به فشار دادن کلید بخوانید. این مستطیل‌ها را به‌صورت سراسری ذخیره نکنید: چیدمان می‌تواند به دلایل زیادی تغییر کند و تعداد کمی از خواندن‌های هندسی هنگام مدیریت یک رویداد کلیدی با سرعت کاربر ارزان است.
  • فیلتر جهت: نامزد فقط زمانی در جهت فشرده‌شده قرار می‌گیرد که لبه نزدیک آن در لبه دور عنصر فعلی در آن محور یا فراتر از آن باشد، که امکان تلورانس کوچکی را برای گرد کردن زیرپیکسل فراهم می‌کند. برای مثال، نامزد right باید candidate.left >= current.right - tolerance داشته باشد. برای تعیین اینکه آیا نامزد در جهت است یا نه، مرکزها را مقایسه نکنید.
  • ابتدا نامزدهای هم‌راستا: از میان نامزدهایی که از فیلتر جهت عبور می‌کنند، نامزدهایی را ترجیح دهید که گستره آن‌ها در محور عمود با گستره عنصر فعلی هم‌پوشانی دارد. کوچک‌ترین فاصله را در محور پیمایش انتخاب کنید و گره‌ها را با کوچک‌ترین انحراف مرکز عمودی بشکنید. این کار باعث می‌شود پیمایش در ردیف یا ستون دیداری مجاور باقی بماند، حتی زمانی که عرض یک مورد به‌طور غیرمعمولی زیاد باشد.
  • امتیازدهی جایگزین: اگر هیچ نامزدی در محور عمودی هم‌پوشانی نداشته باشد، به نامزدهای باقی‌مانده امتیاز gap + w * perpendicularCentreOffset بدهید و کمترین را انتخاب کنید. از وزن w >= 2 به‌عنوان نقطه شروع استفاده کنید، سپس آن را برای چیدمان تنظیم کنید؛ وزن بزرگ‌تر برای نامزدهایی که هم‌ترازی نزدیک‌تری دارند مناسب‌تر است.
  • No candidate: چیزی برنگرداند. گرداننده رویداد نباید 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 برای پیمایش فضایی خودداری کنید. کلیدهای جهت‌نما کلیدهای نویسه نیستند و حفظ پیمایش بومی وقتی نامزدی وجود ندارد از مسدود شدن غیرضروری حرکت نمای هدف جلوگیری می‌کند.