Kiểm tra dữ liệu và thuộc tính formaction

Bài này đi sâu vào cơ chế kiểm tra dữ liệu (constraint validation) của trình duyệt: hiển thị trạng thái hợp lệ bằng CSS, tùy biến thông báo lỗi bằng JavaScript, và các thuộc tính form, formaction, formmethod cho phép một form có nhiều nút làm việc khác nhau.

40 phútNâng cao
Bạn sẽ học được
  • Hiểu quy trình kiểm tra dữ liệu khi gửi form
  • Định dạng trạng thái bằng :valid, :invalid, :user-invalid
  • Tùy biến thông báo bằng setCustomValidity
  • Dùng form, formaction, formmethod, formtarget, formnovalidate
Thuật ngữ cần biết
Constraint validation
Cơ chế trình duyệt kiểm tra required, type, pattern, min, max...
ValidityState
Đối tượng cho biết lỗi gì: valueMissing, typeMismatch, patternMismatch...
Pseudo-class
Bộ chọn CSS theo trạng thái như :invalid.

Khái niệm và lý thuyết

Khi bấm gửi, trình duyệt kiểm tra từng trường theo các ràng buộc; trường đầu tiên không hợp lệ được tô sáng kèm bong bóng thông báo và form không được gửi. Mỗi trường có thuộc tính validity cho biết loại lỗi; JavaScript có thể đọc để hiển thị thông báo tiếng Việt rõ ràng hơn.

Thuộc tính validityXảy ra khi
valueMissingBỏ trống trường required
typeMismatchSai định dạng email, url
patternMismatchKhông khớp pattern
tooShort / tooLongVi phạm minlength / maxlength
rangeUnderflow / rangeOverflowNhỏ hơn min / lớn hơn max
stepMismatchKhông đúng bước step
customErrorLỗi do setCustomValidity đặt

Hiển thị trạng thái bằng CSS

<style>
  .f-kt input { padding: 6px; border: 2px solid #ccc; border-radius: 6px; }
  .f-kt input:user-invalid { border-color: #e53935; background: #ffebee; }
  .f-kt input:valid:not(:placeholder-shown) { border-color: #43a047; }
  .f-kt input:focus { outline: 3px solid #90caf9; }
</style>
<form class="f-kt" onsubmit="alert('Hợp lệ'); return false;">
  <input type="email" name="email" placeholder="Email" required>
  <input name="ma" placeholder="Mã 6 số" pattern="[0-9]{6}" required>
  <button>Gửi</button>
</form>
Kết quả hiển thị

:user-invalid chỉ tô đỏ sau khi người dùng đã tương tác, tránh báo lỗi ngay khi vừa mở trang như :invalid.

Thông báo lỗi tiếng Việt

<form id="f-mk" onsubmit="alert('Đăng ký thành công'); return false;">
  <p><input type="password" id="mk1" placeholder="Mật khẩu (≥ 8 ký tự)" minlength="8" required></p>
  <p><input type="password" id="mk2" placeholder="Nhập lại mật khẩu" required></p>
  <p id="loi-mk" style="color:#c62828"></p>
  <button>Đăng ký</button>
</form>
<script>
  (() => {
    const mk1 = document.getElementById("mk1"), mk2 = document.getElementById("mk2");
    const loi = document.getElementById("loi-mk");
    function kiemTra() {
      mk2.setCustomValidity(mk1.value === mk2.value ? "" : "Mật khẩu nhập lại không khớp");
      if (mk1.validity.tooShort) loi.textContent = "Mật khẩu cần ít nhất 8 ký tự";
      else loi.textContent = mk2.validationMessage;
    }
    mk1.addEventListener("input", kiemTra);
    mk2.addEventListener("input", kiemTra);
  })();
</script>
Kết quả hiển thị

setCustomValidity("") với chuỗi rỗng nghĩa là hợp lệ; chuỗi khác rỗng là lỗi và form sẽ không gửi được. Có thể gọi form.checkValidity() hoặc reportValidity() để kiểm tra chủ động.

Một form, nhiều nút: formaction và bạn bè

<form action="/luu-bai.php" method="post" onsubmit="return false;">
  <textarea name="noi_dung" rows="2" cols="40" required>Bài viết nháp</textarea><br>
  <button type="submit" onclick="alert('Gửi tới /luu-bai.php (POST)')">Xuất bản</button>
  <button type="submit" formaction="/luu-nhap.php" formnovalidate onclick="alert('Gửi tới /luu-nhap.php, bỏ qua kiểm tra')">Lưu nháp</button>
  <button type="submit" formaction="/xem-truoc.php" formmethod="get" formtarget="_blank" onclick="alert('Mở xem trước trong tab mới (GET)')">Xem trước</button>
</form>
Kết quả hiển thị

Thuộc tính trên nút/inputGhi đè thuộc tính của form
formactionaction
formmethodmethod
formtargettarget
formenctypeenctype
formnovalidatenovalidate

Trường nằm ngoài form

<form id="f-dang-ky" onsubmit="alert('Email: ' + new FormData(this).get('email')); return false;">
  <button>Gửi</button>
</form>
<p>Ô này nằm ngoài thẻ form nhưng vẫn thuộc form nhờ thuộc tính form:
  <input type="email" name="email" form="f-dang-ky" value="Địa chỉ email này đang được bảo vệ từ spam bots, bạn cần kích hoạt Javascript để xem nó. Bạn cần kích hoạt Javascript để xem nó."></p>
Kết quả hiển thị

Ô này nằm ngoài thẻ form nhưng vẫn thuộc form nhờ thuộc tính form:

Mẹo hay

Hiển thị lỗi ngay dưới ô (như ví dụ mật khẩu) và gắn bằng aria-describedby để trình đọc màn hình đọc cả thông báo lỗi.

Lưu ý

Kiểm tra ở trình duyệt chỉ là lớp tiện ích. Kẻ xấu có thể gửi dữ liệu thẳng tới máy chủ bằng công cụ như curl, Postman, vì vậy máy chủ luôn phải kiểm tra lại.

Thực hành
  1. Tô đỏ ô sai, tô xanh ô đúng cho form đăng ký bằng :user-invalid và :valid.
  2. Viết kiểm tra “Ngày kết thúc phải sau ngày bắt đầu” bằng setCustomValidity.
  3. Tạo form bài viết có 3 nút: Xuất bản, Lưu nháp, Xem trước.
  4. Đặt một ô tìm kiếm ở header thuộc về form nằm ở thân trang.
Tóm tắt bài học
  • Trình duyệt kiểm tra ràng buộc trước khi gửi.
  • CSS :valid, :invalid, :user-invalid hiển thị trạng thái.
  • validity, setCustomValidity, reportValidity tùy biến kiểm tra.
  • formaction, formmethod, formtarget, formnovalidate, form giúp form linh hoạt.