← HTML: cấu trúc & semantic

Bài 4 · Vận dụng · 22 phút

Bảng & biểu mẫu (form)

Biên soạn bởi Nguyễn Anh Tuấn

Bảng table (thead/tbody/th scope) cho dữ liệu; biểu mẫu form với input gắn label đúng (for/id), các loại input, button và validation cơ bản của trình duyệt.

Có những nội dung tự nhiên xếp thành hàng và cột: bảng điểm, bảng giá, lịch học. Với loại dữ liệu đó, HTML có thẻ <table>. Một bảng gồm các hàng <tr> (table row), trong mỗi hàng là các ô. Ô tiêu đề là <th>, ô dữ liệu thường là <td>.

Bảng gọn gàng nên chia hai phần: <thead> chứa hàng tiêu đề, <tbody> chứa các hàng dữ liệu.

TênTuổiMón thíchMun2Cá khôBông1Sữa ấmhàng trên = th (ô tiêu đề) · các ô dưới = td (ô dữ liệu)mỗi hàng là một tr; bảng chỉ dùng cho dữ liệu thật
Một bảng: hàng tiêu đề (th) ở trên, các hàng dữ liệu (td) ở dưới.

bang.html · bảng dữ liệu với thead và tbody (mở .html bằng trình duyệt)

<table>
  <thead>
    <tr>
      <th>Tên</th>
      <th>Tuổi</th>
      <th>Món thích</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Mun</td>
      <td>2</td>
      <td>Cá khô</td>
    </tr>
    <tr>
      <td>Bông</td>
      <td>1</td>
      <td>Sữa ấm</td>
    </tr>
  </tbody>
</table>
  • table chứa các hàng tr; mỗi hàng chứa các ô.
  • th là ô tiêu đề (tên cột/hàng); td là ô dữ liệu thường.
  • thead gom hàng tiêu đề, tbody gom các hàng dữ liệu.

Thêm một chi tiết nhỏ làm bảng dễ đọc hơn nhiều với người khiếm thị: thuộc tính scope trên thẻ th. Nó cho biết ô tiêu đề này là của một cột (scope="col") hay một hàng (scope="row"), để screen reader đọc mỗi ô dữ liệu kèm đúng tiêu đề của nó.

scope.html · báo th thuộc cột hay hàng

<tr>
  <th scope="col">Tên</th>
  <th scope="col">Tuổi</th>
</tr>

Trung thực

Ngày xưa người ta hay dùng bảng để dàn bố cục trang cho thẳng hàng. Đừng làm vậy nữa: bảng chỉ dành cho DỮ LIỆU hàng-cột. Dàn bố cục là việc của CSS (học ở khoá sau); dùng bảng sai mục đích khiến trang khó sửa, khó co giãn trên điện thoại và làm screen reader đọc lộn xộn - đúng kiểu lạm dụng thẻ mà bài Semantic HTML là gì cảnh báo.

Tới giờ trang chỉ HIỆN nội dung ra. Muốn trang nhận thông tin từ người dùng (đăng ký, đăng nhập, gửi góp ý), ta cần biểu mẫu - thẻ <form>. Hãy hình dung form như một tờ phiếu giấy: có nhiều ô để điền và một nút để nộp.

Ô điền phổ biến nhất là <input>. Cùng một thẻ input nhưng đổi type sẽ ra nhiều loại ô khác nhau: text (chữ), email (địa chỉ email), password (mật khẩu, hiện dấu chấm), number, date… Cuối form là một <button> để gửi.

form-co-ban.html · vài loại input và một nút gửi

<form>
  <input type="text" />
  <input type="email" />
  <input type="password" />
  <button type="submit">Gửi</button>
</form>
  • form bọc các trường nhập và nút gửi - như một tờ phiếu.
  • input là ô nhập; đổi type để ra ô chữ, email, mật khẩu, số, ngày…
  • button type="submit" là nút nộp form.

Một ô input trơ trọi thì người dùng không biết phải điền gì. Mỗi ô cần một nhãn - thẻ <label>. Nhưng đặt chữ cạnh ô thôi chưa đủ: phải nối nhãn với ô bằng cặp thuộc tính for (của label) trùng với id (của input).

Emailfor="email"[ ô nhập ]id="email"khớp nhauBấm vào chữ "Email" -> con trỏ nhảy thẳng vào ô nhậpscreen reader cũng đọc đúng "Email" khi tới ô này
for của label trùng id của input thì hai thứ được nối với nhau.

label.html · nối label với input qua for/id

<form>
  <label for="ten">Tên của mèo con</label>
  <input type="text" id="ten" />

  <label for="email">Email</label>
  <input type="email" id="email" />
</form>

Liên kết này mang lại hai điều: bấm vào chữ nhãn thì con trỏ nhảy thẳng vào ô (vùng bấm rộng hơn, rất tiện trên điện thoại), và screen reader đọc đúng tên trường cho người khiếm thị - cùng tinh thần với thuộc tính alt cho ảnh ở bài Liên kết, ảnh & đa phương tiện. Đây cũng là một viên gạch của a11y: web cho mọi người - một thẻ nhỏ, khác biệt lớn.

  • Mỗi input cần một label cho biết phải nhập gì.
  • Nối label với input bằng for (label) trùng id (input).
  • Nối đúng thì bấm label là focus vào ô, và screen reader đọc đúng tên trường.
  • placeholder chỉ là gợi ý mờ trong ô, KHÔNG thay được label.

Điều bất ngờ dễ chịu: trình duyệt có sẵn một lớp kiểm tra cơ bản (validation) mà chưa cần JavaScript. Thêm required thì ô đó bắt buộc phải điền; đặt type="email" thì trình duyệt tự kiểm địa chỉ có đúng dạng email không khi bấm gửi.

validation.html · bắt buộc và kiểm định dạng, không cần JS

<form>
  <label for="ten">Tên</label>
  <input type="text" id="ten" required />

  <label for="email">Email</label>
  <input type="email" id="email" required />

  <button type="submit">Đăng ký</button>
</form>

Trung thực

Validation của trình duyệt giúp form thân thiện với người dùng, nhưng KHÔNG phải lớp bảo mật. Dữ liệu gửi đi vẫn phải được kiểm lại ở phía máy chủ - thứ mèo con sẽ gặp ở các khoá backend sau này. Ở mức HTML, đây là cách làm form tử tế mà chưa cần dòng JavaScript nào.

Bước tiếp theo

Giờ mèo con đã dựng được bảng và biểu mẫu - những khối có cấu trúc rõ ràng. Nhưng làm sao để cả TRANG có cấu trúc đúng nghĩa? Bài kế ta học khái niệm trung tâm của khoá: Semantic HTML là gì & vì sao quan trọng.

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

th (table header) là ô TIÊU ĐỀ - tên của một cột hoặc một hàng; trình duyệt thường in đậm và canh giữa nó. td (table data) là ô chứa DỮ LIỆU thường. Dùng th cho ô tiêu đề giúp người đọc lẫn screen reader biết mỗi ô dữ liệu thuộc về tiêu đề nào.

Không. Bảng chỉ dành cho DỮ LIỆU dạng hàng-cột (bảng điểm, bảng giá…). Dùng bảng để dàn bố cục là thói quen cũ đã bỏ: nó làm screen reader đọc sai, khó sửa, và khó co giãn trên điện thoại. Việc dàn bố cục là của CSS, học ở khoá sau.

Khi for của label trùng id của input, hai thứ được nối với nhau: bấm vào chữ label thì con trỏ nhảy ngay vào ô nhập (vùng bấm to hơn, tiện trên điện thoại), và screen reader đọc đúng tên trường khi người khiếm thị tới ô đó. Thiếu liên kết này thì ô nhập trở thành một ô vô danh.

Không. placeholder là dòng chữ mờ gợi ý BÊN TRONG ô, nó biến mất khi bắt đầu gõ - nên không thể thay cho label. Hãy luôn có label riêng; placeholder chỉ là gợi ý thêm (vd "vd: meo@email.com").

Nó tiện cho NGƯỜI DÙNG (báo lỗi ngay khi bỏ trống required hay gõ sai định dạng email), nhưng không phải lớp bảo mật. Dữ liệu vẫn phải được kiểm lại ở phía máy chủ - thứ học ở các khoá backend. Ở mức HTML, validation sẵn có giúp form thân thiện hơn mà chưa cần một dòng JavaScript.

Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.

Tick những điều em tự tin làm được sau khi học bài này. 0/6

Trả lời vài câu để chắc rằng em đã nắm bài.

Câu 1/3 Điểm: 0

Trong bảng, thẻ nào dùng cho ô TIÊU ĐỀ (tên cột hoặc hàng)?

Bài tập về nhà

  1. 1

    Bảng điểm của mèo con

    Dựng một bảng 3 cột (Môn, Điểm, Xếp loại) với hàng tiêu đề và 2 hàng dữ liệu, dùng thead và tbody.

    ✅ Hoàn thành khi: Có <table> với <thead> (một <tr> chứa 3 <th>) và <tbody> (2 <tr>, mỗi tr 3 <td>).

  2. 2

    Thêm scope

    Thêm scope cho các th trong hàng tiêu đề của bảng trên.

    ✅ Hoàn thành khi: Mỗi <th> tiêu đề cột có scope="col".

  3. 3

    Bảng hay không bảng?

    Với mỗi thứ, nên dùng bảng table hay không: (a) bảng giá 3 gói dịch vụ; (b) chia trang thành cột trái - cột phải cho đẹp.

    ✅ Hoàn thành khi: (a) dùng table vì là dữ liệu hàng-cột; (b) KHÔNG dùng table - đó là bố cục, để CSS lo.

  4. 4

    Một form đăng ký

    Viết một form có ô nhập Tên, ô nhập Email và một nút "Đăng ký", mỗi ô nhập có label gắn đúng.

    ✅ Hoàn thành khi: Có <form> chứa 2 cặp label+input (for trùng id) và một <button> gửi.

  5. 5

    Bắt buộc và đúng định dạng

    Sửa form trên để ô Tên bắt buộc nhập, và ô Email phải đúng định dạng email.

    ✅ Hoàn thành khi: input Tên có thuộc tính required; input Email có type="email".

  6. 6

    Sửa lỗi label rời

    Cho <label>Email</label><input id="mail" />. Chỉ ra vì sao label này chưa nối với ô nhập và sửa lại.

    ✅ Hoàn thành khi: Nêu: label thiếu for; sửa thành <label for="mail">Email</label> để for trùng id="mail".