Chuyển đến nội dung chính
Tất cả bài viết

15 tháng 9, 2026·11 phút đọc

Khả năng tiếp cận của tiêu đề bảng: quy tắc WCAG mà hầu hết website bỏ sót

Bảng dữ liệu không có đánh dấu tiêu đề thật được trình đọc màn hình đọc thành những con số vô nghĩa. Xem cách sửa WCAG 1.3.1 và những gì Axeazy có thể và không thể tự động sửa.

wcagaccessibilitytablestable-headerswcag-1-3-1axe-corescreen-readerhtml

Một bảng dữ liệu không có đánh dấu tiêu đề thật được trình đọc màn hình đọc thành một chuỗi những con số trơ trụi, không biết con số nào thuộc hàng hay cột nào. Cách sửa là dùng <th> thay cho <td> ở các ô tiêu đề và thêm thuộc tính scope, điều này đáp ứng WCAG 1.3.1 (Thông tin và Mối quan hệ) và biến một bảng chỉ là bố cục trực quan thành thứ mà trình đọc màn hình thực sự có thể điều hướng.

Bảng dữ liệu trông bình thường về mặt hình ảnh nhưng được trình đọc màn hình đọc thành những con số không có nhãn.

Những điểm chính

  • <th> và <td> trông giống hệt nhau nếu bạn tạo kiểu giống nhau, nhưng chỉ <th> mới báo cho trình đọc màn hình "đây là tiêu đề". Chữ đậm, căn giữa trong <td> vô hình với công nghệ hỗ trợ như một tiêu đề, bất kể nó trông thế nào.
  • Thuộc tính scope cho trình đọc màn hình biết tiêu đề áp dụng theo hướng nào. Giá trị hợp lệ là col, row, colgroup và rowgroup, không có gì khác.
  • Đây là yêu cầu của WCAG 1.3.1 (Thông tin và Mối quan hệ), Cấp độ A. Nó cũng tương ứng với 1194.22(g) của Section 508 và 9.1.3.1 của EN 301 549, nên không chỉ là quy tắc của riêng Hoa Kỳ.
  • axe-core quét bảy vấn đề riêng biệt của bảng, và chỉ một trong số đó, giá trị scope gõ sai, hiện có bản sửa tự động. Phần còn lại cần một người hiểu bảng thực sự có nghĩa là gì.
  • Bảng đơn giản (tiêu đề chỉ ở hàng đầu hoặc cột đầu) cần ít đánh dấu hơn so với những gì hầu hết hướng dẫn ngụ ý. Chỉ cần các phần tử <th> thuần là đủ; scope trở nên cần thiết khi bảng có tiêu đề ở nhiều hơn một hướng.

Vì sao <th> so với <td> là tất cả

Hai tiêu đề bảng trông giống hệt nhau, một cái dựng bằng td và một cái bằng th, chỉ một trong hai được trình đọc màn hình nhận là tiêu đề.

Khách truy cập nhìn thấy được đọc bảng bằng cách lướt qua một hàng và nhìn lên tiêu đề cột mà không cần nghĩ. Trình đọc màn hình không có cách tương đương để "liếc lên". Nó đọc từng ô một, và cách duy nhất để biết một ô là tiêu đề, chứ không chỉ là một mẩu dữ liệu khác, là phần tử HTML bên dưới.

<th> là ô tiêu đề. Trình duyệt mặc định in đậm và căn giữa nó, và trình đọc màn hình đọc nó là tiêu đề khi người dùng điều hướng bảng. <td> là ô dữ liệu thường. Nó không có thông báo như vậy, dù áp dụng CSS gì lên nó.

Đây là chỗ hầu hết mọi bảng trong thực tế bị hỏng. Một lập trình viên dựng bảng, muốn hàng tiêu đề trông đậm, và dùng <td style="font-weight: bold"> thay vì <th>. Về mặt hình ảnh, nó không thể phân biệt với một tiêu đề thật. Với trình đọc màn hình, nó chỉ là một ô dữ liệu khác tình cờ in đậm, không có mối liên hệ nào với các ô bên dưới.

<!-- Lỗi: trông như hàng tiêu đề, được đọc là dữ liệu thường -->
<table>
  <tr>
    <td><strong>Day</strong></td>
    <td><strong>Open</strong></td>
    <td><strong>Close</strong></td>
  </tr>
  <tr>
    <td>Monday</td>
    <td>9:00 AM</td>
    <td>6:00 PM</td>
  </tr>
</table>

<!-- Đạt: ô tiêu đề thật, được đọc là tiêu đề, kèm chú thích -->
<table>
  <caption>Store hours</caption>
  <tr>
    <th scope="col">Day</th>
    <th scope="col">Open</th>
    <th scope="col">Close</th>
  </tr>
  <tr>
    <td>Monday</td>
    <td>9:00 AM</td>
    <td>6:00 PM</td>
  </tr>
</table>

Cả hai hiển thị giống hệt nhau với khách truy cập nhìn thấy được. Chỉ cái thứ hai cho người dùng trình đọc màn hình biết rằng "9:00 AM" thuộc về "Open" vào "Monday".

Thuộc tính scope và khi nào bạn thực sự cần nó

Sơ đồ cho thấy scope=col chỉ xuống một cột và scope=row chỉ ngang một hàng trong bảng dữ liệu.

Với bảng đơn giản, chỉ có tiêu đề ở hàng đầu, hoặc chỉ ở cột đầu, không phải cả hai, chỉ <th> là đủ. Trình đọc màn hình có thể suy ra đúng hướng. Đây là trường hợp mà yêu cầu WCAG nghiêm ngặt thực sự nhẹ hơn so với nhiều nội dung về khả năng tiếp cận ngụ ý.

Khi bảng có tiêu đề chạy theo hai hướng cùng lúc, chẳng hạn một lịch với các ngày ở trên và các khung giờ ở bên cạnh, scope không còn là tùy chọn trong thực tế. Thiếu nó, trình đọc màn hình không có cách đáng tin cậy để biết một <th> nhất định gắn nhãn cho hàng hay cột chứa nó.

<table>
  <caption>Weekly class schedule</caption>
  <tr>
    <th scope="col"></th>
    <th scope="col">Monday</th>
    <th scope="col">Wednesday</th>
  </tr>
  <tr>
    <th scope="row">9:00 AM</th>
    <td>Yoga</td>
    <td>Pilates</td>
  </tr>
  <tr>
    <th scope="row">6:00 PM</th>
    <td>Spin</td>
    <td>Yoga</td>
  </tr>
</table>

Ở đây, scope="col" đánh dấu các tiêu đề ngày và scope="row" đánh dấu các tiêu đề giờ. Người dùng trình đọc màn hình đến ô "Pilates" nghe cả hai: Wednesday, 9:00 AM, Pilates.

Với bảng thực sự không đều, ô gộp, tiêu đề không nằm gọn trong một hàng hay cột, riêng scope là không đủ. Đó là lý do có thuộc tính headers và id: mỗi ô dữ liệu liệt kê rõ các id của mọi ô tiêu đề áp dụng cho nó.

<th id="q3">Q3</th>
...
<td headers="q3">42</td>

Cách này tốn công viết và duy trì hơn, chính vì thế nó được dành cho các bảng mà scope thực sự không mô tả được, chứ không dùng làm mặc định.

axe-core thực sự kiểm tra gì trong một bảng

axe-core, cùng bộ máy đứng sau bảng khả năng tiếp cận của Chrome DevTools và trình quét của Axeazy, chạy bảy phép kiểm tra riêng biệt trên đánh dấu bảng. Chỉ một trong số đó hiện có bản sửa tự động.

Quy tắc Kiểm tra gì Cách sửa
scope-attr-valid Giá trị scope không phải từ thật, gõ sai hoặc số nhiều của col/row/colgroup/rowgroup Tự động sửa qua pull request GitHub, phạm vi hẹp: chỉ các lỗi gõ sai nhận ra được
empty-table-header Có <th> nhưng bên trong không có chữ Thủ công
th-has-data-cells Có <th> nhưng không ô dữ liệu nào dùng nó Thủ công
td-has-header Một ô dữ liệu không có tiêu đề nào liên kết Thủ công
td-headers-attr Thuộc tính headers trỏ đến một id không phải ô tiêu đề thật Thủ công
table-duplicate-name Hai bảng trên cùng một trang có cùng tên truy cập được Thủ công
table-fake-caption Văn bản được tạo kiểu trông như chú thích thay vì phần tử <caption> thật Thủ công

Bảng đó làm rõ bức tranh trung thực. Cấu trúc bảng chủ yếu là nhóm sửa thủ công. Phần duy nhất được viết tự động là sửa giá trị scope đã có nhưng viết sai, ví dụ "cols" hay "ROW " được chuẩn hóa thành "col" hay "row". Thêm tiêu đề còn thiếu, viết chú thích, hay xác định id của headers nào thuộc ô nào đều cần hiểu bảng thực sự có nghĩa gì, và đó vẫn là việc của con người.

Cách kiểm tra các bảng của bạn

Bạn không cần cài trình đọc màn hình để phát hiện phiên bản phổ biến nhất của lỗi này.

  1. Xem mã nguồn trang. Nhấp chuột phải vào bất kỳ bảng nào trên site và chọn Xem nguồn trang, hoặc mở DevTools và xem bảng Elements. Tìm <th. Nếu hàng trông như tiêu đề được làm bằng các phần tử <td>, bảng đó không đạt.
  2. Kiểm tra giá trị scope trên các bảng nhiều hướng. Nếu bảng có tiêu đề chạy cả ngang phía trên và dọc bên cạnh, xác nhận mỗi <th> mang scope="col" hoặc scope="row", không chỉ là kiểu in đậm.
  3. Nghe thử. Trong NVDA, di chuyển qua một bảng được đánh dấu đúng bằng điều hướng bảng (Ctrl+Alt+phím mũi tên) sẽ đọc tiêu đề liên quan cùng giá trị của từng ô. Nếu bạn chỉ nghe thấy các con số thô mà không có tiêu đề được đọc trước, bảng chưa được liên kết đúng.
  4. Hoặc chạy quét thay vì kiểm tra từng bảng. Lượt quét miễn phí của Axeazy kiểm tra mọi bảng trên trang chủ của bạn theo cả bảy quy tắc trên trong khoảng 60 giây, cùng với các nhóm sửa lỗi khác mà nó bao quát.

Những lỗi phổ biến

  • Ô <td> in đậm thay chỗ <th>. Lỗi phổ biến nhất, và khó phát hiện bằng mắt nhất, vì trông hoàn toàn đúng.
  • Chú thích chỉ là một dòng chữ in đậm phía trên bảng, thay vì phần tử <caption> thật. Khách truy cập nhìn thấy được không nhận ra khác biệt; trình đọc màn hình thì có.
  • Nhiều bảng trên một trang không có chú thích phân biệt, nên người dùng trình đọc màn hình không biết bảng nào là bảng nào khi nhảy giữa chúng.
  • Dùng bảng chỉ để bố cục trực quan, không phải dữ liệu dạng bảng. Trình đọc màn hình vẫn cố đọc các mối quan hệ hàng và cột chưa bao giờ có ý định tồn tại, gây rối hơn là không có cấu trúc. CSS hiện đại (Grid hoặc Flexbox) xử lý bố cục mà không có tác dụng phụ này.
  • Dùng scope khi bảng thực sự cần headers/id. scope chỉ mô tả mối quan hệ hàng hoặc cột đơn giản. Bảng có ô gộp hoặc tiêu đề không nằm gọn trong một hàng hay cột cần cách ghép headers/id rõ ràng hơn.

Axeazy có thể sửa gì ở đây

Sơ đồ cho thấy chỉ giá trị scope gõ sai được tự động sửa, còn mọi vấn đề khả năng tiếp cận bảng khác đều được đánh dấu để xem xét thủ công.

Nhóm này nói thật được nhiều hơn hầu hết, vì phần tự động của nó thực sự nhỏ.

Lượt quét của Axeazy đánh dấu cả bảy quy tắc bảng ở trên ngay khi tìm thấy, cùng một cách phát hiện dù là Axeazy, Chrome DevTools hay bất kỳ công cụ nào xây trên axe-core. Viết bản sửa là chỗ trở nên thủ công. Bản sửa liên quan đến bảng duy nhất hiện đến được một pull request GitHub thật là sửa giá trị scope đã có mà là lỗi gõ sai hoặc số nhiều nhận ra được; không có gì khác trong nhóm này có đường tự động.

Hiện cũng không có đường plugin WordPress nào cho các bản sửa bảng. Ba bản sửa tức thì của plugin miễn phí bao gồm điều hướng nhảy qua, thuộc tính ngôn ngữ và văn bản thay thế dự phòng, không cái nào chạm vào đánh dấu bảng, và các gói trả phí hiện cũng không mở rộng đến cấu trúc bảng.

Đó không phải khoảng trống ai đó quên. Quyết định một <td> lẽ ra phải là <th>, hay một ô bất thường thực sự thuộc tiêu đề nào, đòi hỏi hiểu bảng đang muốn truyền đạt điều gì. Một đoạn mã đoán sai ở đây sẽ lặng lẽ làm sai lệch dữ liệu đối với chính những người dựa vào đánh dấu để hiểu nó, tệ hơn việc để nó được đánh dấu cho một người sửa đúng. Xem bảng phân tích đầy đủ các nhóm sửa lỗi.

Câu hỏi thường gặp

Tôi có cần thuộc tính scope trên mọi ô tiêu đề không?

Không. Với bảng đơn giản có tiêu đề chỉ ở hàng đầu hoặc chỉ ở cột đầu, các phần tử <th> thuần là đủ. scope trở nên cần thiết khi bảng có tiêu đề chạy theo hai hướng cùng lúc, như lịch có ngày ở trên và giờ ở bên cạnh.

Khác biệt giữa scope và headers/id là gì?

scope mô tả mối quan hệ đơn giản, một hướng, một tiêu đề áp dụng cho cả hàng, cột hoặc nhóm hàng hay cột. headers/id rõ ràng hơn: mỗi ô dữ liệu liệt kê chính xác ô tiêu đề nào áp dụng cho nó bằng id. Dùng scope cho bảng đều dạng lưới và headers/id chỉ cho bảng không đều có ô gộp hoặc tiêu đề không nằm gọn trong một hàng hay cột.

Axeazy có tự động sửa tiêu đề bảng bị thiếu không?

Phần lớn là không, và đáng nói thẳng như vậy. Bản sửa liên quan đến bảng duy nhất hiện có đường tự động hoạt động là sửa giá trị scope đã có nhưng viết sai. Thêm tiêu đề còn thiếu, viết chú thích thật, hay ghép headers với id đúng đều cần hiểu bảng có nghĩa gì, nên chúng vẫn là mục xem xét thủ công.

Tôi có cần chú thích (caption) cho mọi bảng không?

Bản thân nó không phải yêu cầu nghiêm ngặt của WCAG 1.3.1, nhưng thực sự hữu ích, nhất là khi trang có nhiều hơn một bảng. Chú thích cho bảng tên truy cập được, để người dùng trình đọc màn hình nhảy giữa các bảng biết mình đã đến bảng nào.

Còn những bảng dùng để bố cục trang thay vì dữ liệu thì sao?

Tránh khi có thể. Trình đọc màn hình vẫn cố đọc mối quan hệ hàng và cột trên bảng bố cục, gây nhầm lẫn mà sẽ không có nếu dùng bố cục CSS thuần. Nếu chưa thể loại bỏ ngay một bảng bố cục cũ, đánh dấu nó bằng role="presentation" báo cho công nghệ hỗ trợ bỏ qua hoàn toàn ngữ nghĩa bảng.

Nguồn

Muốn nhận nội dung này qua email?

The ADA Alert. Tin tức hằng tuần.

Mỗi thứ Ba: tin tức khả năng tiếp cận, các lỗi WCAG phổ biến và cách sửa ở cấp mã nguồn. Miễn phí. Hủy đăng ký chỉ với một cú nhấp. Bản tin được viết bằng tiếng Anh.

Phạm vi của mọi báo cáo chúng tôi cung cấp: GIỚI HẠN PHẠM VI: Báo cáo này ghi lại các vi phạm được phát hiện qua quét tự động theo WCAG 2.2 AA bằng axe-core v4.11.0. Công cụ tự động không thể kiểm tra mọi tiêu chí WCAG; nhiều tiêu chí cần được rà soát thủ công. Báo cáo này không phải là tư vấn pháp lý, không bảo đảm tuân thủ ADA và không bảo vệ bạn khỏi hành động pháp lý. Việc tuân thủ ADA là một kết luận pháp lý do tòa án đưa ra. Để đánh giá đầy đủ mức độ phù hợp với WCAG, hãy thuê một chuyên gia về khả năng tiếp cận có chứng chỉ IAAP.