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.
- 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
- 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 validity | Xảy ra khi |
|---|---|
| valueMissing | Bỏ trống trường required |
| typeMismatch | Sai định dạng email, url |
| patternMismatch | Không khớp pattern |
| tooShort / tooLong | Vi phạm minlength / maxlength |
| rangeUnderflow / rangeOverflow | Nhỏ hơn min / lớn hơn max |
| stepMismatch | Không đúng bước step |
| customError | Lỗ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>: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>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>| Thuộc tính trên nút/input | Ghi đè thuộc tính của form |
|---|---|
| formaction | action |
| formmethod | method |
| formtarget | target |
| formenctype | enctype |
| formnovalidate | novalidate |
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>Ô này nằm ngoài thẻ form nhưng vẫn thuộc form nhờ thuộc tính form:
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.
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.
- Tô đỏ ô sai, tô xanh ô đúng cho form đăng ký bằng :user-invalid và :valid.
- Viết kiểm tra “Ngày kết thúc phải sau ngày bắt đầu” bằng setCustomValidity.
- Tạo form bài viết có 3 nút: Xuất bản, Lưu nháp, Xem trước.
- Đặt một ô tìm kiếm ở header thuộc về form nằm ở thân trang.
- 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.