Điều hướng không gian (Tiêu điểm bàn phím định hướng)

Tính năng web: scrollIntoView()

Tính năng điều hướng tiêu điểm theo hướng (sử dụng phím mũi tên hoặc D-Pad) cho phép người dùng chỉ sử dụng bàn phím và người dùng sử dụng phương thức nhập thay thế điều hướng các mục tương tác dựa trên bố cục 2D trực quan của các mục đó. Tính năng này rất hữu ích cho giao diện người dùng không gian (chẳng hạn như dải nội dung nghe nhìn, bảng điều khiển hoặc danh mục lồng nhau), nhưng phải được triển khai cẩn thận để tránh làm hỏng các hành vi gốc của trình duyệt như cuộn khung nhìn.

Hướng dẫn này trình bày cách xây dựng tính năng điều hướng theo hướng 2D hiệu quả và dễ tiếp cận bên trong các tiện ích kết hợp.


Nguyên tắc cấu trúc cốt lõi

Khi triển khai tiêu điểm không gian bằng phím mũi tên tuỳ chỉnh, hãy tuân thủ các quy tắc cơ bản sau:

  1. Giới hạn ở các tiện ích kết hợp: Đừng ghi đè các phím mũi tên trên luồng văn bản tài liệu thông thường hoặc bố cục dạng danh sách dọc đơn giản, nơi dự kiến điều hướng Tab tuần tự mặc định. Hãy xem Nguyên tắc lập trình về hỗ trợ tiếp cận để biết hướng dẫn chung về luồng tài liệu.
  2. Giữ nguyên thao tác cuộn ranh giới gốc: Nếu không có phần tử nào có thể làm tiêu điểm theo hướng được nhấn, thì đừng ngăn hành vi của trình duyệt mặc định. Hãy để sự kiện nổi lên để trình duyệt có thể cuộn khung nhìn theo cách tự nhiên. Việc ghi đè các phím mũi tên vô điều kiện sẽ làm hỏng khả năng hỗ trợ tiếp cận (WCAG 1.4.10).
  3. Đảm bảo chế độ hiển thị và căn chỉnh tiêu điểm: Khi tiêu điểm thay đổi, hãy đảm bảo phần tử mới được lấy tiêu điểm sẽ được cuộn vào khung hiển thị (ví dụ: sử dụng scrollIntoView({ block: 'nearest', inline: 'nearest' })) để phần tử đó vẫn hiển thị đầy đủ.
  4. Phối hợp với hệ thống bố cục: Xác định vị trí của ứng cử viên tiêu điểm từ hình học giới hạn trực tiếp (getBoundingClientRect()) trong khi xử lý mỗi lần nhấn phím. Tránh giả định rằng thứ tự DOM hoặc cấu trúc lưới cố định thể hiện bố cục trực quan. Hãy xem bài viết Bố cục CSS và thiết kế đáp ứng để biết các đề xuất về bố cục hiện đại.

Quản lý tiêu điểm dễ tiếp cận

Vùng chứa không gian phải hiển thị một điểm dừng thẻ duy nhất cho thao tác điều hướng tuần tự bằng phím Tab trên trang. Bên trong tiện ích, hãy quản lý tiêu điểm bàn phím bằng Mẫu chỉ mục thẻ di chuyển:

  • Mục đang hoạt động có tabindex="0".
  • Tất cả các mục không hoạt động đều có tabindex="-1".
  • Khi di chuyển, hãy di chuyển tiêu điểm theo phương thức lập trình bằng cách chuyển thuộc tính tabindex="0" và gọi focus().

Lựa chọn đề xuất trực quan

Trong bố cục không đối xứng, được gói hoặc dạng lưới, việc ánh xạ dựa trên chỉ mục sẽ không thành công. Thay vào đó, hãy lấy các đề xuất từ hình học trực quan trực tiếp cho lần nhấn phím hiện tại:

  • Ứng viên: Sử dụng các thành phần con có thể làm tâm điểm của vùng chứa tiện ích truyền checkVisibility(). Các phần tử ẩn có thể có hình chữ nhật kích thước bằng 0 tại (0, 0) và không được trở thành mục tiêu up hoặc left ngoài ý muốn.
  • Hình học: Đọc getBoundingClientRect() cho phần tử được lấy tiêu điểm và mọi phần tử đề xuất trong khi xử lý thao tác nhấn phím. Đừng lưu các hình chữ nhật này vào bộ nhớ đệm trên toàn hệ thống: bố cục có thể thay đổi vì nhiều lý do và một số ít lượt đọc hình học sẽ không tốn nhiều tài nguyên khi xử lý sự kiện chính theo tốc độ của người dùng.
  • Bộ lọc hướng: Một đề xuất chỉ nằm theo hướng được nhấn khi cạnh gần của đề xuất đó nằm tại hoặc vượt quá cạnh xa của phần tử hiện tại trên trục đó, cho phép dung sai nhỏ để làm tròn điểm ảnh phụ. Ví dụ: ứng viên cho right phải có candidate.left >= current.right - tolerance. Đừng so sánh các tâm để xác định xem một ứng viên có nằm trong hướng hay không.
  • Ưu tiên các ứng cử viên thẳng hàng: Trong số các ứng cử viên vượt qua bộ lọc hướng, hãy ưu tiên các ứng cử viên có phạm vi trên trục vuông góc trùng lặp với phạm vi của phần tử hiện tại. Chọn khoảng cách nhỏ nhất trên trục điều hướng, phá vỡ các mối liên kết bằng độ lệch tâm vuông góc nhỏ nhất. Điều này giúp thao tác điều hướng luôn nằm trong hàng hoặc cột hình ảnh liền kề ngay cả khi một mục có chiều rộng bất thường.
  • Điểm dự phòng: Nếu không có ứng viên nào trùng lặp trên trục vuông góc, hãy chấm điểm cho các ứng viên còn lại là gap + w * perpendicularCentreOffset và chọn giá trị nhỏ nhất. Sử dụng trọng số w >= 2 làm điểm xuất phát, sau đó điều chỉnh trọng số cho bố cục; trọng số lớn hơn sẽ ưu tiên các đề xuất phù hợp hơn.
  • Không có đề xuất: Không trả về kết quả nào. Trình xử lý sự kiện không được gọi preventDefault(), cho phép trình duyệt cuộn theo cách tự nhiên.

Cách triển khai chính xác có thể khác nhau, nhưng hãy giữ cho hướng kiểm thử dựa trên cạnh và áp dụng lựa chọn ưu tiên chồng chéo trước khi dự phòng có trọng số. Đây là điểm mấu chốt của thuật toán; tránh trình bày bộ nhớ đệm hoặc trọng số cố định là chính xác trên toàn hệ thống.


Tích hợp thao tác nhấn phím và phối hợp thao tác cuộn

Bộ định tuyến sự kiện của bạn phải ngăn việc cuộn khung nhìn tiêu chuẩn chỉ khi quá trình chuyển đổi tiêu điểm không gian hợp lệ diễn ra:

  • Không chặn sự kiện khi phím bổ trợ được giữ, khi e.defaultPrevented đã là true hoặc khi mục tiêu sự kiện trong cùng tự sử dụng các phím mũi tên. Sử dụng e.composedPath()[0] thay vì chỉ e.target, vì e.target có thể được nhắm mục tiêu lại đến một máy chủ bóng. Một cách kiểm tra khả năng chỉnh sửa/kiểm soát thực tế là target.matches(':read-write, select, input[type="range"], [role="slider"]'); không thể kiểm tra các gốc bóng tối đã đóng.
  • Xác định phạm vi của trình nghe cho vùng chứa tiện ích để nhiều tiện ích không gian có thể cùng tồn tại trên một trang.
  • Chỉ hành động khi phần tử được lấy tiêu điểm chính là một đề xuất. Nếu các mục chứa các chế độ điều khiển có thể làm tâm điểm của riêng chúng, hãy đặt các chế độ điều khiển đó làm ứng cử viên hoặc để yên; không giải quyết phần tử mẹ bằng closest().
  • Khi tìm thấy một đề xuất, hãy gọi preventDefault(), cập nhật tabindex chuyển vùng, gọi focus() và sử dụng scrollIntoView({ block: 'nearest', inline: 'nearest' }).
  • Khi không tìm thấy đề xuất nào, hãy trả về mà không gọi preventDefault() để tiếp tục thao tác cuộn gốc.

Tránh thêm các phím tắt một ký tự như h/j/k/l hoặc W/A/S/D để điều hướng không gian. Các phím mũi tên không phải là phím ký tự và việc giữ nguyên thao tác cuộn gốc khi không có đề xuất nào sẽ giúp tránh chặn thao tác di chuyển khung nhìn một cách không cần thiết.