Using HTML form validation and the Constraint Validation API
Việc tạo biểu mẫu web luôn là một tác vụ phức tạp. Trong khi việc đánh dấu bản thân biểu mẫu thì dễ dàng, việc kiểm tra xem mỗi trường có giá trị hợp lệ và nhất quán hay không thì khó hơn, và việc thông báo cho người dùng về vấn đề có thể trở thành một bài toán đau đầu. HTML5 đã giới thiệu các cơ chế mới cho biểu mẫu: nó đã thêm các loại ngữ nghĩa mới cho phần tử <input> và constraint validation để dễ dàng kiểm tra nội dung biểu mẫu ở phía client. Các ràng buộc cơ bản, thông thường có thể được kiểm tra mà không cần JavaScript, bằng cách đặt các thuộc tính mới; các ràng buộc phức tạp hơn có thể được kiểm tra bằng cách sử dụng Constraint Validation API.
Để có phần giới thiệu cơ bản về các khái niệm này, với ví dụ, hãy xem hướng dẫn xác thực biểu mẫu.
Note: HTML Constraint validation không loại bỏ nhu cầu xác thực ở phía server. Mặc dù ít yêu cầu biểu mẫu không hợp lệ hơn được mong đợi, các yêu cầu không hợp lệ vẫn có thể được gửi theo nhiều cách:
- Bằng cách sửa đổi HTML qua công cụ dành cho nhà phát triển của trình duyệt.
- Bằng cách tạo thủ công một yêu cầu HTTP mà không sử dụng biểu mẫu.
- Bằng cách lập trình viết nội dung vào biểu mẫu (một số xác thực ràng buộc chỉ chạy cho đầu vào của người dùng, và không chạy nếu bạn đặt giá trị của trường biểu mẫu bằng JavaScript).
Do đó, bạn nên luôn xác thực dữ liệu biểu mẫu ở phía server, nhất quán với những gì được thực hiện ở phía client.
Ràng buộc nội tại và cơ bản
Trong HTML, các ràng buộc cơ bản được khai báo theo hai cách:
- Bằng cách chọn giá trị ngữ nghĩa phù hợp nhất cho thuộc tính
typecủa phần tử<input>, ví dụ, chọn loạiemailtự động tạo ra một ràng buộc kiểm tra xem giá trị có phải là địa chỉ email hợp lệ hay không. - Bằng cách đặt giá trị trên các thuộc tính liên quan đến xác thực, cho phép mô tả các ràng buộc cơ bản mà không cần JavaScript.
Loại input ngữ nghĩa
Các ràng buộc nội tại cho thuộc tính type là:
| Loại Input | Mô tả ràng buộc | Vi phạm liên quan |
|---|---|---|
<input type="URL"> |
Giá trị phải là URL tuyệt đối, như được định nghĩa trong URL Living Standard. | Vi phạm ràng buộc TypeMismatch |
<input type="email"> |
Giá trị phải là địa chỉ email hợp lệ về mặt cú pháp, thường có dạng username@hostname.tld nhưng cũng có thể là địa phương như username@hostname. |
Vi phạm ràng buộc TypeMismatch |
Đối với loại input email, nếu thuộc tính multiple được đặt, có thể đặt nhiều giá trị dưới dạng danh sách phân cách bởi dấu phẩy. Nếu bất kỳ giá trị nào trong danh sách không thỏa mãn điều kiện được mô tả ở đây, vi phạm ràng buộc Type mismatch sẽ được kích hoạt.
Lưu ý rằng hầu hết các loại input không có ràng buộc nội tại, vì một số bị ngăn khỏi constraint validation hoặc có thuật toán làm sạch chuyển đổi các giá trị không đúng sang giá trị mặc định đúng.
Thuộc tính liên quan đến xác thực
Ngoài thuộc tính type được mô tả ở trên, các thuộc tính sau được sử dụng để mô tả các ràng buộc cơ bản:
| Thuộc tính | Loại input hỗ trợ thuộc tính | Giá trị có thể | Mô tả ràng buộc | Vi phạm liên quan |
|---|---|---|---|---|
pattern
|
text, search, url,
tel, email, password
|
Một
biểu thức chính quy JavaScript
(được biên dịch với các cờ global, ignoreCase, và
multiline bị vô hiệu hóa)
|
Giá trị phải khớp với pattern. |
Vi phạm ràng buộc
patternMismatch
|
min
|
range, number |
Một số hợp lệ | Giá trị phải lớn hơn hoặc bằng giá trị. |
Vi phạm ràng buộc
rangeUnderflow
|
date, month, week |
Một ngày hợp lệ | |||
datetime-local, time
|
Một ngày và giờ hợp lệ | |||
max
|
range, number |
Một số hợp lệ | Giá trị phải nhỏ hơn hoặc bằng giá trị |
Vi phạm ràng buộc
rangeOverflow
|
date, month, week |
Một ngày hợp lệ | |||
datetime-local, time
|
Một ngày và giờ hợp lệ | |||
required
|
text, search, url,
tel, email, password,
date, datetime-local,
month, week, time,
number, checkbox, radio,
file; cũng trên các phần tử <select> và
<textarea>
|
không có vì đây là thuộc tính Boolean: sự hiện diện của nó có nghĩa là true, sự vắng mặt của nó có nghĩa là false | Phải có giá trị (nếu được đặt). |
Vi phạm ràng buộc
valueMissing
|
step
|
date |
Một số nguyên số ngày |
Trừ khi step được đặt thành literal any, giá trị phải
là min + bội số nguyên của step.
|
Vi phạm ràng buộc
stepMismatch
|
month |
Một số nguyên số tháng | |||
week |
Một số nguyên số tuần | |||
datetime-local, time
|
Một số nguyên số giây | |||
range, number |
Một số nguyên | |||
minlength
|
text, search, url,
tel, email, password; cũng trên
phần tử <textarea>
|
Độ dài nguyên |
Số ký tự (code point) không được ít hơn giá trị
của thuộc tính, nếu không rỗng. Tất cả các ký tự xuống dòng được chuẩn hóa thành một
ký tự duy nhất (thay vì cặp CRLF) cho
<textarea>.
|
Vi phạm ràng buộc
tooShort
|
maxlength
|
text, search, url,
tel, email, password; cũng trên
phần tử <textarea>
|
Độ dài nguyên | Số ký tự (code point) không được vượt quá giá trị của thuộc tính. |
Vi phạm ràng buộc
tooLong
|
Quy trình constraint validation
Constraint validation được thực hiện thông qua Constraint Validation API trên một phần tử biểu mẫu đơn lẻ hoặc ở mức độ biểu mẫu, trên chính phần tử <form>. Constraint validation được thực hiện theo các cách sau:
- Bằng một lệnh gọi đến phương thức
checkValidity()hoặcreportValidity()của interface DOM liên quan đến biểu mẫu, (HTMLInputElement,HTMLSelectElement,HTMLButtonElement,HTMLOutputElementhoặcHTMLTextAreaElement), chỉ đánh giá các ràng buộc trên phần tử này, cho phép script lấy thông tin này. Phương thứccheckValidity()trả về Boolean cho biết giá trị của phần tử có vượt qua các ràng buộc của nó hay không. (Điều này thường được thực hiện bởi user-agent khi xác định pseudo-class CSS nào,:validhay:invalid, áp dụng.) Ngược lại, phương thứcreportValidity()báo cáo bất kỳ sự cố ràng buộc nào cho người dùng. - Bằng một lệnh gọi đến phương thức
checkValidity()hoặcreportValidity()trên interfaceHTMLFormElement. - Bằng cách gửi chính biểu mẫu.
Gọi checkValidity() được gọi là tĩnh để xác thực các ràng buộc, trong khi gọi reportValidity() hoặc gửi biểu mẫu được gọi là tương tác để xác thực các ràng buộc.
Note:
- Nếu thuộc tính
novalidateđược đặt trên phần tử<form>, xác thực ràng buộc tương tác sẽ không xảy ra. - Gọi phương thức
submit()trên interfaceHTMLFormElementkhông kích hoạt constraint validation. Nói cách khác, phương thức này gửi dữ liệu biểu mẫu đến server ngay cả khi nó không thỏa mãn các ràng buộc. Thay vào đó, hãy gọi phương thứcclick()trên nút submit. - Các ràng buộc
minlengthvàmaxlengthchỉ được kiểm tra trên đầu vào do người dùng cung cấp. Chúng không được kiểm tra nếu giá trị được đặt theo cách lập trình, ngay cả khi gọicheckValidity()hoặcreportValidity()một cách rõ ràng.
Các ràng buộc phức tạp sử dụng Constraint Validation API
Sử dụng JavaScript và Constraint API, có thể triển khai các ràng buộc phức tạp hơn, ví dụ, các ràng buộc kết hợp nhiều trường, hoặc các ràng buộc liên quan đến các phép tính phức tạp.
Về cơ bản, ý tưởng là kích hoạt JavaScript trên một số sự kiện trường biểu mẫu (như onchange) để tính toán xem ràng buộc có bị vi phạm hay không, và sau đó sử dụng phương thức field.setCustomValidity() để đặt kết quả của việc xác thực: một chuỗi rỗng có nghĩa là ràng buộc được thỏa mãn, và bất kỳ chuỗi nào khác có nghĩa là có một lỗi và chuỗi này là thông báo lỗi để hiển thị cho người dùng.
Ràng buộc kết hợp nhiều trường: Xác thực mã bưu chính
Định dạng mã bưu chính thay đổi theo từng quốc gia. Nhiều quốc gia cho phép tiền tố tùy chọn với mã quốc gia (như D- ở Đức, F- ở Pháp, và CH- ở Thụy Sĩ). Một số quốc gia chỉ sử dụng số cố định trong mã bưu chính, trong khi các quốc gia khác, như Anh, có các định dạng phức tạp hơn cho phép chữ cái ở một số vị trí cụ thể.
Note: Đây không phải là thư viện xác thực mã bưu chính toàn diện, mà là minh họa các khái niệm chính.
Ví dụ, chúng ta sẽ thêm một script kiểm tra constraint validation cho một biểu mẫu:
<form>
<label for="postal-code">Postal Code: </label>
<input type="text" id="postal-code" />
<label for="country">Country: </label>
<select id="country">
<option value="ch">Switzerland</option>
<option value="fr">France</option>
<option value="de">Germany</option>
<option value="nl">The Netherlands</option>
</select>
<input type="submit" value="Validate" />
</form>
Điều này hiển thị biểu mẫu sau:
Đầu tiên, chúng ta viết một hàm kiểm tra chính ràng buộc:
const countrySelect = document.getElementById("country");
const postalCodeField = document.getElementById("postal-code");
function checkPostalCode() {
// For each country, defines the pattern that the postal code has to follow
const constraints = {
ch: [
"^(CH-)?\\d{4}$",
"Swiss postal codes must have exactly 4 digits: e.g. CH-1950 or 1950",
],
fr: [
"^(F-)?\\d{5}$",
"French postal codes must have exactly 5 digits: e.g. F-75012 or 75012",
],
de: [
"^(D-)?\\d{5}$",
"German postal codes must have exactly 5 digits: e.g. D-12345 or 12345",
],
nl: [
"^(NL-)?\\d{4}\\s*([A-RT-Z][A-Z]|S[BCE-RT-Z])$",
"Dutch postal codes must have exactly 4 digits, followed by 2 letters except SA, SD and SS",
],
};
// Read the country id
const country = countrySelect.value;
// Build the constraint checker
const constraint = new RegExp(constraints[country][0], "");
console.log(constraint);
// Check it!
if (constraint.test(postalCodeField.value)) {
// The postal code follows the constraint, we use the ConstraintAPI to tell it
postalCodeField.setCustomValidity("");
} else {
// The postal code doesn't follow the constraint, we use the ConstraintAPI to
// give a message about the format required for this country
postalCodeField.setCustomValidity(constraints[country][1]);
}
}
Sau đó chúng ta liên kết nó với sự kiện change cho <select> và sự kiện input cho <input>:
countrySelect.addEventListener("change", checkPostalCode);
postalCodeField.addEventListener("input", checkPostalCode);
Giới hạn kích thước tệp trước khi tải lên
Một ràng buộc phổ biến khác là giới hạn kích thước tệp được tải lên. Kiểm tra điều này ở phía client trước khi tệp được truyền đến server yêu cầu kết hợp Constraint Validation API, và đặc biệt là phương thức field.setCustomValidity(), với một API JavaScript khác, ở đây là File API.
Đây là phần HTML:
<label for="fs">Select a file smaller than 75 kB: </label>
<input type="file" id="fs" />
Điều này hiển thị:
JavaScript đọc tệp được chọn, sử dụng phương thức File.size() để lấy kích thước của nó, so sánh với giới hạn (được hardcode), và gọi Constraint API để thông báo cho trình duyệt nếu có vi phạm:
const fs = document.getElementById("fs");
function checkFileSize() {
const files = fs.files;
// If there is (at least) one file selected
if (files.length > 0) {
if (files[0].size > 75 * 1000) {
// Check the constraint
fs.setCustomValidity("The selected file must not be larger than 75 kB");
fs.reportValidity();
return;
}
}
// No custom constraint violation
fs.setCustomValidity("");
}
Cuối cùng, chúng ta hook phương thức với sự kiện đúng:
fs.addEventListener("change", checkFileSize);
Tạo kiểu trực quan cho constraint validation
Ngoài việc đặt các ràng buộc, các nhà phát triển web muốn kiểm soát những thông báo nào được hiển thị cho người dùng và cách chúng được tạo kiểu.
Kiểm soát giao diện của các phần tử
Giao diện của các phần tử có thể được kiểm soát thông qua các pseudo-class CSS.
Pseudo-class CSS :required và :optional
Các pseudo-class :required và :optional cho phép viết các selector khớp với các phần tử biểu mẫu có thuộc tính required, hoặc không có nó.
Pseudo-class CSS :placeholder-shown
Xem :placeholder-shown.
Pseudo-class CSS :valid :invalid
Các pseudo-class :valid và :invalid được sử dụng để biểu diễn các phần tử <input> có nội dung xác thực và không xác thực tương ứng theo cài đặt loại của input. Các lớp này cho phép người dùng tạo kiểu cho các phần tử biểu mẫu hợp lệ hoặc không hợp lệ để giúp dễ nhận biết các phần tử được định dạng đúng hoặc sai.
Kiểm soát văn bản của vi phạm ràng buộc
Các mục sau có thể giúp kiểm soát văn bản của vi phạm ràng buộc:
-
Phương thức
setCustomValidity(message)trên các phần tử sau:<fieldset>. Lưu ý: Đặt thông báo hợp lệ tùy chỉnh trên các phần tử fieldset sẽ không ngăn việc gửi biểu mẫu trong hầu hết các trình duyệt.<input><output><select>- Các nút submit (được tạo bằng phần tử
<button>với loạisubmit, hoặc phần tửinputvới loại submit. Các loại nút khác không tham gia vào constraint validation. <textarea>
-
Interface
ValidityStatemô tả đối tượng được trả về bởi thuộc tínhvaliditycủa các loại phần tử được liệt kê ở trên. Nó biểu diễn các cách khác nhau mà một giá trị được nhập có thể không hợp lệ. Kết hợp với nhau, chúng giúp giải thích lý do tại sao giá trị của một phần tử không xác thực được, nếu nó không hợp lệ.