Sử dụng chỉ mục thẻ

Sửa đổi thứ tự DOM bằng chỉ mục tab

Dave Gash
Dave Gash
Meggin Kearney
Meggin Kearney

Thứ tự thẻ mặc định do vị trí DOM của các phần tử gốc cung cấp là rất thuận tiện, nhưng đôi khi bạn muốn sửa đổi thứ tự thẻ và việc di chuyển các phần tử trong HTML không phải lúc nào cũng là giải pháp tối ưu hay thậm chí là khả thi. Đối với những trường hợp này, bạn có thể dùng thuộc tính HTML tabindex để đặt vị trí thẻ của một phần tử một cách rõ ràng.

Hỗ trợ trình duyệt

  • 1
  • 12
  • 1,5
  • ≤4

Nguồn

tabindex có thể được áp dụng cho bất kỳ phần tử nào, mặc dù mã này không nhất thiết hữu ích trên mọi phần tử và cần một dải giá trị số nguyên. Bằng cách sử dụng tabindex, bạn có thể chỉ định thứ tự rõ ràng cho các phần tử trang có thể làm tâm điểm, chèn một phần tử không thể làm tâm điểm vào thứ tự thẻ và xoá các phần tử khỏi thứ tự thẻ. Ví dụ:

tabindex="0": Chèn một phần tử vào thứ tự thẻ thông thường. Bạn có thể lấy phần tử làm tâm điểm bằng cách nhấn phím Tab. Bạn có thể lấy tiêu điểm phần tử bằng cách gọi phương thức focus()

<custom-button tabindex="0">Press Tab to Focus Me!</custom-button>

Nhấn phím Tab để lấy nét cho tôi!

tabindex="-1": Xoá một phần tử khỏi thứ tự thẻ thông thường, nhưng vẫn có thể lấy phần tử đó làm tâm điểm bằng cách gọi phương thức focus()

// TODO: DevSite - Code sample removed as it used inline event handlers

// VIỆC CẦN LÀM: DevSite - Mã mẫu bị xoá vì đã sử dụng trình xử lý sự kiện nội tuyến

tabindex="5": Bất kỳ chỉ mục thẻ nào lớn hơn 0 sẽ chuyển phần tử lên phía trước thứ tự thẻ thông thường. Nếu có nhiều phần tử có chỉ mục thẻ lớn hơn 0, thì thứ tự thẻ sẽ bắt đầu từ giá trị thấp nhất lớn hơn 0 và hoạt động tăng dần. Việc sử dụng chỉ mục thẻ lớn hơn 0 được xem là phản mẫu.

<button>I should be first</button>
<button>And I should be second</button>
<button tabindex="5">But I jumped to the front!</button>

Điều này đặc biệt đúng với các phần tử không phải dữ liệu đầu vào như tiêu đề, hình ảnh hoặc tiêu đề bài viết. Việc thêm tabindex vào các loại phần tử đó sẽ phản tác dụng. Nếu có thể, tốt nhất là bạn nên sắp xếp mã nguồn để trình tự DOM cung cấp thứ tự thẻ logic. Nếu bạn sử dụng tabindex, hãy giới hạn ở các thành phần điều khiển tương tác tuỳ chỉnh như nút, thẻ, trình đơn thả xuống và trường văn bản; tức là các phần tử mà người dùng có thể muốn cung cấp dữ liệu đầu vào.

Đừng lo lắng về việc người dùng trình đọc màn hình bỏ lỡ nội dung quan trọng vì trình đọc màn hình không có tabindex. Ngay cả khi nội dung rất quan trọng, chẳng hạn như hình ảnh, nếu nội dung đó không phải là nội dung mà người dùng có thể tương tác thì không có lý do gì để khiến nội dung đó có thể làm tâm điểm. Người dùng trình đọc màn hình vẫn có thể hiểu được nội dung của hình ảnh miễn là bạn hỗ trợ thích hợp cho thuộc tính alt. Chúng tôi sẽ trình bày nội dung này ngay sau đây.

Quản lý tiêu điểm ở cấp trang

Dưới đây là trường hợp tabindex không chỉ hữu ích mà còn cần thiết. Có thể bạn đang xây dựng một trang mạnh mẽ với nhiều phần nội dung, nhưng không phải tất cả các phần này đều hiển thị cùng lúc. Trong loại trang này, việc nhấp vào đường liên kết điều hướng có thể thay đổi nội dung hiển thị mà không cần làm mới trang.

Khi điều này xảy ra, có thể bạn sẽ xác định vùng nội dung đã chọn, cung cấp tabindex là -1 để vùng đó không xuất hiện theo thứ tự thẻ thông thường và gọi phương thức focus. Kỹ thuật này được gọi là quản lý tiêu điểm, giúp ngữ cảnh trực quan của người dùng được đồng bộ với nội dung hình ảnh của trang web.

Quản lý tiêu điểm trong các thành phần

Việc quản lý tiêu điểm khi bạn thay đổi nội dung nào đó trên trang là rất quan trọng, nhưng đôi khi bạn cần quản lý tiêu điểm ở cấp kiểm soát – ví dụ: nếu bạn đang tạo một thành phần tuỳ chỉnh.

Hãy xem xét phần tử select gốc. Giao diện này có thể nhận tiêu điểm cơ bản, nhưng khi ở đó, bạn có thể sử dụng các phím mũi tên để hiển thị chức năng bổ sung (các tuỳ chọn có thể chọn). Nếu đang tạo một phần tử select tuỳ chỉnh, bạn nên hiển thị các loại hành vi tương tự này để người dùng chủ yếu sử dụng bàn phím vẫn có thể tương tác với chế độ điều khiển của bạn.

<!-- Focus the element using Tab and use the up/down arrow keys to navigate -->
<select>
    <option>Aisle seat</option>
    <option>Window seat</option>
    <option>No preference</option>
</select>

Không dễ để triển khai hành vi bàn phím nào, nhưng bạn có thể tham khảo tài liệu hữu ích. Hướng dẫn về Phương pháp soạn thảo ứng dụng Internet đa dạng thức dễ tiếp cận (ARIA) liệt kê các loại thành phần và loại thao tác trên bàn phím mà các thành phần đó hỗ trợ. Chúng ta sẽ đề cập chi tiết hơn về ARIA sau, nhưng hiện tại, hãy dùng hướng dẫn để thêm tính năng hỗ trợ bàn phím vào một thành phần mới.

Có thể bạn đang nghiên cứu một số Phần tử tuỳ chỉnh mới giống với một tập hợp các nút chọn, nhưng với giao diện và hành vi độc đáo của bạn.

<radio-group>
    <radio-button>Water</radio-button>
    <radio-button>Coffee</radio-button>
    <radio-button>Tea</radio-button>
    <radio-button>Cola</radio-button>
    <radio-button>Ginger Ale</radio-button>
</radio-group>

Để xác định loại hỗ trợ bàn phím mà họ cần, bạn nên xem Hướng dẫn về phương pháp tạo tác giả ARIA. Phần 2 chứa danh sách các mẫu thiết kế và trong danh sách đó là bảng đặc điểm cho các nhóm radio, thành phần hiện có phù hợp nhất với phần tử mới của bạn.

Như bạn có thể thấy trong bảng, một trong những hành vi phổ biến của bàn phím cần được hỗ trợ là các phím mũi tên lên/xuống/trái/phải. Để thêm hành vi này vào thành phần mới, chúng ta sẽ sử dụng kỹ thuật có tên là roving tabindex (chỉ mục thẻ lưu trữ).

Phần trích dẫn thông số W3C cho các nút chọn.

Chỉ mục thẻ lưu trữ hoạt động bằng cách đặt tabindex thành -1 cho tất cả thành phần con, ngoại trừ thành phần con đang hoạt động.

<radio-group>
    <radio-button tabindex="0">Water</radio-button>
    <radio-button tabindex="-1">Coffee</radio-button>
    <radio-button tabindex="-1">Tea</radio-button>
    <radio-button tabindex="-1">Cola</radio-button>
    <radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>

Sau đó, thành phần này sử dụng trình nghe sự kiện trên bàn phím để xác định phím mà người dùng nhấn; khi điều này xảy ra, thành phần này sẽ đặt tabindex của thành phần con được lấy làm tiêu điểm trước đó thành -1, đặt tabindex của thành phần con được lấy làm tiêu điểm thành 0 và gọi phương thức lấy nét trên đó.

<radio-group>
    // Assuming the user pressed the down arrow, we'll focus the next available child
    <radio-button tabindex="-1">Water</radio-button>
    <radio-button tabindex="0">Coffee</radio-button> // call .focus() on this element
    <radio-button tabindex="-1">Tea</radio-button>
    <radio-button tabindex="-1">Cola</radio-button>
    <radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>

Khi người dùng xem đến thành phần con cuối cùng (hoặc đầu tiên, tuỳ thuộc vào hướng mà họ đang di chuyển tiêu điểm), bạn sẽ lặp lại và lấy tiêu điểm con đầu tiên (hoặc cuối cùng) một lần nữa.

Bạn có thể thử ví dụ hoàn chỉnh dưới đây. Kiểm tra phần tử trong Công cụ phát triển để quan sát thấy chỉ mục thẻ di chuyển từ đài phát này sang đài phát thanh kế tiếp.

Nước Cà phê Trà Chai Đồ uống có gai

// VIỆC CẦN LÀM: DevSite - Mã mẫu bị xoá vì đã sử dụng trình xử lý sự kiện nội tuyến

Bạn có thể xem nguồn đầy đủ cho phần tử này trên GitHub.

Các mô-đun và bẫy bàn phím

Đôi khi, khi đang quản lý tiêu điểm, bạn có thể rơi vào một tình huống mà bạn không thể thoát ra. Hãy xem xét một tiện ích tự động hoàn thành giúp cố gắng quản lý tiêu điểm và chụp lại hành vi của thẻ, nhưng ngăn người dùng rời khỏi thẻ cho đến khi hoàn tất. Đây được gọi là bẫy bàn phím và có thể gây khó chịu cho người dùng. Mục 2.1.2 của danh sách kiểm tra trên Web Album giải quyết vấn đề này, nêu rõ rằng tiêu điểm bàn phím không bao giờ bị khoá hoặc mắc kẹt tại một phần tử trang cụ thể. Người dùng phải có thể di chuyển đến và từ tất cả các thành phần trang chỉ bằng bàn phím.

Kỳ lạ thay, đôi khi hành vi này thực sự mong muốn, chẳng hạn như trong cửa sổ phương thức. Thông thường, khi cửa sổ phụ được hiển thị, bạn không muốn người dùng truy cập vào nội dung phía sau cửa sổ đó. Bạn có thể thêm một lớp phủ để che phủ trang một cách trực quan, nhưng điều đó không ngăn tiêu điểm bàn phím vô tình di chuyển ra ngoài cửa sổ phụ.

Một cửa sổ phụ yêu cầu người dùng lưu công việc của họ.

Trong những trường hợp như vậy, bạn có thể triển khai một bẫy bàn phím tạm thời để đảm bảo rằng bạn chỉ bẫy tiêu điểm khi cửa sổ phụ được hiển thị, sau đó khôi phục tiêu điểm về mục đã lấy tiêu điểm trước đó khi cửa sổ phụ được đóng.

Có một số đề xuất về cách giúp nhà phát triển dễ thực hiện việc này, bao gồm cả phần tử <dialog>, nhưng chưa hỗ trợ trình duyệt rộng rãi.

Hãy xem bài viết MDN này để biết thêm thông tin về <dialog>ví dụ về phương thức này để biết thêm thông tin về cửa sổ phụ.

Hãy xem xét hộp thoại phương thức được biểu thị bằng div chứa một vài phần tử và một div khác đại diện cho lớp phủ nền. Hãy cùng tìm hiểu các bước cơ bản cần thiết để triển khai một bẫy bàn phím tạm thời trong trường hợp này.

  1. Sử dụng document.querySelector, chọn các div cửa sổ phụ và lớp phủ, đồng thời lưu trữ các tệp tham chiếu của chúng.
  2. Khi cửa sổ phụ mở ra, hãy lưu trữ tệp tham chiếu đến phần tử đã được lấy tiêu điểm khi cửa sổ phụ mở để bạn có thể trả về tiêu điểm cho phần tử đó.
  3. Sử dụng trình nghe phím tắt để lấy các phím khi được nhấn trong khi cửa sổ phụ đang mở. Bạn cũng có thể theo dõi lượt nhấp vào lớp phủ nền và đóng cửa sổ phụ nếu người dùng nhấp vào.
  4. Tiếp theo, hãy nhận tập hợp các phần tử có thể làm tâm điểm trong cửa sổ phụ. Các phần tử có thể làm tâm điểm đầu tiên và cuối cùng sẽ đóng vai trò là "nhân viên gửi" để cho bạn biết thời điểm cần lặp lại tiêu điểm tiến hoặc lùi để luôn ở bên trong cửa sổ phụ.
  5. Hiển thị cửa sổ phụ và lấy tiêu điểm là phần tử có thể làm tâm điểm đầu tiên.
  6. Khi người dùng nhấn Tab hoặc Shift+Tab, hãy di chuyển tiêu điểm về phía trước hoặc phía sau, lặp lại ở các phần tử cuối cùng hoặc đầu tiên cho phù hợp.
  7. Nếu người dùng nhấn Esc, hãy đóng cửa sổ phụ. Cách này rất hữu ích vì cho phép người dùng đóng cửa sổ phụ mà không cần phải tìm kiếm một nút đóng cụ thể. Tính năng này cũng mang lại lợi ích cho cả những người dùng đang sử dụng chuột.
  8. Khi phương thức được đóng, hãy ẩn phương thức đó và lớp phủ nền, đồng thời khôi phục tiêu điểm về phần tử được lấy tiêu điểm trước đó đã lưu trước đó.

Quy trình này cung cấp cho bạn một cửa sổ cửa sổ phụ có thể sử dụng và không gây khó chịu mà mọi người đều có thể sử dụng hiệu quả.

Để biết thêm thông tin chi tiết, bạn có thể kiểm tra mã mẫu này và xem ví dụ trực tiếp trên trang đã hoàn tất.