Ngày xuất bản: 8 tháng 8 năm 2019
Nhiều nhà phát triển tạo các chế độ kiểm soát biểu mẫu tuỳ chỉnh, hoặc để cung cấp các chế độ kiểm soát không được tích hợp vào trình duyệt, hoặc để tuỳ chỉnh giao diện vượt quá khả năng của các chế độ kiểm soát biểu mẫu tích hợp.
Tuy nhiên, bạn có thể gặp khó khăn khi sao chép các tính năng của các chế độ điều khiển biểu mẫu HTML tích hợp. Hãy cân nhắc một số tính năng mà phần tử <input> sẽ tự động nhận được khi bạn thêm phần tử đó vào biểu mẫu:
- Dữ liệu đầu vào sẽ tự động được thêm vào danh sách điều khiển của biểu mẫu.
- Giá trị đầu vào sẽ được tự động gửi kèm theo biểu mẫu.
- Đầu vào tham gia vào xác thực biểu mẫu. Bạn có thể định kiểu đầu vào bằng cách sử dụng các lớp giả
:validvà:invalid. - Đầu vào sẽ được thông báo khi biểu mẫu được đặt lại, khi biểu mẫu được tải lại hoặc khi trình duyệt cố gắng tự động điền các mục nhập biểu mẫu.
Các điều khiển biểu mẫu tùy chỉnh thường có ít tính năng này. Các nhà phát triển có thể khắc phục một số hạn chế trong JavaScript, chẳng hạn như thêm <input> ẩn vào biểu mẫu để tham gia gửi biểu mẫu. Nhưng các tính năng khác không thể được sao chép chỉ bằng JavaScript.
Hai tính năng web giúp xây dựng các điều khiển biểu mẫu tùy chỉnh dễ dàng hơn và loại bỏ những hạn chế của các điều khiển tùy chỉnh:
- Sự kiện
formdatacho phép một đối tượng JavaScript tùy ý tham gia vào quá trình gửi biểu mẫu, do đó bạn có thể thêm dữ liệu biểu mẫu mà không cần sử dụng<input>ẩn. - API phần tử tùy chỉnh liên quan đến biểu mẫu cho phép các phần tử tùy chỉnh hoạt động giống như các điều khiển biểu mẫu tích hợp sẵn.
Hai tính năng này có thể được sử dụng để tạo ra các loại điều khiển mới hoạt động tốt hơn.
API dựa trên sự kiện
Sự kiện formdata là API cấp thấp cho phép bất kỳ mã JavaScript nào tham gia vào việc gửi biểu mẫu.
- Thêm trình lắng nghe sự kiện
formdatavào biểu mẫu mà bạn muốn tương tác. - Khi người dùng nhấp vào nút gửi, biểu mẫu sẽ kích hoạt một sự kiện
formdata, bao gồm một đối tượngFormDatachứa tất cả dữ liệu đang được gửi. - Mỗi trình lắng nghe
formdatađều có cơ hội thêm hoặc sửa đổi dữ liệu trước khi biểu mẫu được gửi.
Sau đây là ví dụ về cách gửi một giá trị duy nhất trong trình lắng nghe sự kiện formdata:
const form = document.querySelector('form');
// FormData event is sent on <form> submission, before transmission.
// The event has a formData property
form.addEventListener('formdata', ({formData}) => {
// https://developer.mozilla.org/docs/Web/API/FormData
formData.append('my-input', myInputValue);
});
Các thành phần tùy chỉnh liên quan đến biểu mẫu
Bạn có thể sử dụng API dựa trên sự kiện với bất kỳ loại thành phần nào, nhưng nó chỉ cho phép bạn tương tác với quy trình gửi.
Các điều khiển biểu mẫu chuẩn hóa tham gia vào nhiều phần của vòng đời biểu mẫu. Các phần tử tùy chỉnh liên quan đến biểu mẫu nhằm mục đích thu hẹp khoảng cách giữa các tiện ích tùy chỉnh và các điều khiển tích hợp. Các thành phần tùy chỉnh liên quan đến biểu mẫu phù hợp với nhiều tính năng của các thành phần biểu mẫu chuẩn hóa:
- Khi bạn đặt một phần tử tùy chỉnh liên kết với biểu mẫu bên trong
<form>, phần tử đó sẽ tự động được liên kết với biểu mẫu, giống như một điều khiển do trình duyệt cung cấp. - Phần tử có thể được gắn nhãn bằng phần tử
<label>. - Phần tử có thể thiết lập giá trị được tự động gửi kèm theo biểu mẫu.
- Phần tử có thể thiết lập cờ để cho biết dữ liệu đầu vào có hợp lệ hay không. Nếu một trong các điều khiển biểu mẫu có dữ liệu đầu vào không hợp lệ, biểu mẫu sẽ không thể được gửi.
- Phần tử này có thể cung cấp lệnh gọi lại cho nhiều phần khác nhau của vòng đời biểu mẫu, chẳng hạn như khi biểu mẫu bị vô hiệu hóa hoặc được đặt lại về trạng thái mặc định.
- Phần tử này hỗ trợ các pseudoclass CSS chuẩn cho các điều khiển biểu mẫu, chẳng hạn như
:disabledvà:invalid.
Tài liệu này không đề cập đến mọi thứ nhưng mô tả những điều cơ bản cần thiết để tích hợp phần tử tùy chỉnh của bạn vào biểu mẫu.
Xác định một phần tử tùy chỉnh liên quan đến biểu mẫu
Để biến một phần tử tùy chỉnh thành một phần tử tùy chỉnh liên kết với biểu mẫu, cần thực hiện thêm một vài bước:
- Thêm thuộc tính
formAssociatedtĩnh vào lớp phần tử tùy chỉnh của bạn. Điều này yêu cầu trình duyệt xử lý phần tử như một điều khiển biểu mẫu. - Gọi phương thức
attachInternals()trên phần tử để truy cập vào các phương thức và thuộc tính bổ sung cho các điều khiển biểu mẫu, nhưsetFormValue()vàsetValidity(). - Thêm các thuộc tính và phương thức chung được các điều khiển biểu mẫu hỗ trợ, như
name,valuevàvalidity.
Sau đây là cách các mục đó phù hợp với định nghĩa phần tử tùy chỉnh cơ bản:
// Form-associated custom elements must be autonomous custom elements.
// They must extend HTMLElement, not one of its subclasses.
class MyCounter extends HTMLElement {
// Identify the element as a form-associated custom element
static formAssociated = true;
constructor() {
super();
// Get access to the internal form control APIs
this.internals_ = this.attachInternals();
// internal value for this control
this.value_ = 0;
}
// Form controls usually expose a "value" property
get value() { return this.value_; }
set value(v) { this.value_ = v; }
// The following properties and methods aren't strictly required,
// but browser-level form controls provide them. Providing them helps
// ensure consistency with browser-provided controls.
get form() { return this.internals_.form; }
get name() { return this.getAttribute('name'); }
get type() { return this.localName; }
get validity() {return this.internals_.validity; }
get validationMessage() {return this.internals_.validationMessage; }
get willValidate() {return this.internals_.willValidate; }
checkValidity() { return this.internals_.checkValidity(); }
reportValidity() {return this.internals_.reportValidity(); }
…
}
customElements.define('my-counter', MyCounter);
Sau khi đăng ký, bạn có thể sử dụng phần tử này bất cứ nơi nào bạn sử dụng điều khiển biểu mẫu do trình duyệt cung cấp:
<form>
<label>Number of bunnies: <my-counter></my-counter></label>
<button type="submit">Submit</button>
</form>
Đặt giá trị
Phương thức attachInternals() trả về một đối tượng ElementInternals cung cấp quyền truy cập vào các API điều khiển biểu mẫu. Phương thức cơ bản nhất trong số này là phương thức setFormValue(), dùng để thiết lập giá trị hiện tại của điều khiển.
Phương thức setFormValue() có thể nhận một trong 3 loại giá trị:
- Giá trị chuỗi.
- Đối tượng
File. - Đối tượng
FormData. Bạn có thể sử dụng đối tượngFormDatađể truyền nhiều giá trị. Ví dụ, kiểm soát đầu vào thẻ tín dụng có thể truyền số thẻ, ngày hết hạn và mã xác minh.
Cách đặt giá trị:
this.internals_.setFormValue(this.value_);
Để đặt nhiều giá trị, bạn có thể làm như sau:
// Use the control's name as the base name for submitted data
const n = this.getAttribute('name');
const entries = new FormData();
entries.append(n + '-first-name', this.firstName_);
entries.append(n + '-last-name', this.lastName_);
this.internals_.setFormValue(entries);
Xác thực dữ liệu đầu vào
Kiểm soát của bạn cũng có thể tham gia xác thực biểu mẫu bằng cách gọi phương thức setValidity() trên đối tượng nội bộ.
// Assume this is called whenever the internal value is updated
onUpdateValue() {
if (!this.matches(':disabled') && this.hasAttribute('required') &&
this.value_ < 0) {
this.internals_.setValidity({customError: true}, 'Value cannot be negative.');
}
else {
this.internals_.setValidity({});
}
this.internals.setFormValue(this.value_);
}
Bạn có thể tạo kiểu cho một phần tử tuỳ chỉnh được liên kết với biểu mẫu bằng các giả lớp :valid và :invalid, giống như một chế độ kiểm soát biểu mẫu tích hợp.
Gọi lại vòng đời
API phần tử tùy chỉnh liên quan đến biểu mẫu bao gồm một tập hợp các lệnh gọi lại vòng đời bổ sung để liên kết với vòng đời của biểu mẫu. Các lệnh gọi lại là tùy chọn: chỉ triển khai lệnh gọi lại nếu phần tử của bạn cần thực hiện một hành động nào đó tại thời điểm đó trong vòng đời.
void formAssociatedCallback(form)
Được gọi khi trình duyệt liên kết phần tử với một phần tử biểu mẫu hoặc tách phần tử khỏi một phần tử biểu mẫu.
void formDisabledCallback(disabled)
Được gọi sau khi trạng thái disabled của phần tử thay đổi, có thể là do thuộc tính disabled của phần tử này đã được thêm hoặc xóa; hoặc do trạng thái disabled đã thay đổi trên <fieldset> là phần tử tổ tiên của phần tử này.
Ví dụ, phần tử có thể vô hiệu hóa các phần tử trong DOM bóng của nó khi nó bị vô hiệu hóa.
void formResetCallback()
Được gọi sau khi biểu mẫu được thiết lập lại. Phần tử này sẽ tự thiết lập lại về trạng thái mặc định. Đối với các phần tử <input>, điều này thường liên quan đến việc thiết lập thuộc tính value để khớp với thuộc tính value được đặt trong đánh dấu. Với hộp đánh dấu, điều này liên quan đến việc đặt thuộc tính checked sao cho khớp với thuộc tính checked.
void formStateRestoreCallback(state, mode)
Được gọi trong một trong hai trường hợp sau:
- Khi trình duyệt khôi phục trạng thái của phần tử, chẳng hạn như sau khi điều hướng hoặc khi trình duyệt khởi động lại. Đối số
modelà"restore". - Khi các tính năng hỗ trợ nhập liệu của trình duyệt như tự động điền biểu mẫu đặt một giá trị. Đối số
modelà"autocomplete".
Loại đối số đầu tiên phụ thuộc vào cách phương thức setFormValue() được gọi.
Khôi phục trạng thái biểu mẫu
Trong một số trường hợp, chẳng hạn như khi quay lại trang hoặc khởi động lại trình duyệt, trình duyệt có thể cố gắng khôi phục biểu mẫu về trạng thái mà người dùng đã để lại.
Đối với phần tử tùy chỉnh liên quan đến biểu mẫu, trạng thái được khôi phục sẽ đến từ giá trị bạn truyền cho phương thức setFormValue(). Bạn có thể gọi phương thức bằng một tham số giá trị duy nhất, như được hiển thị trong các ví dụ trước đó, hoặc bằng hai tham số:
this.internals_.setFormValue(value, state);
value đại diện cho giá trị có thể gửi của chế độ cài đặt. Tham số state không bắt buộc là một biểu thị nội bộ về trạng thái của chế độ kiểm soát, có thể bao gồm dữ liệu không được gửi đến máy chủ. Tham số state có cùng kiểu dữ liệu với tham số value: chuỗi, đối tượng File hoặc FormData.
Tham số state sẽ hữu ích khi bạn không thể khôi phục trạng thái của một chế độ kiểm soát chỉ dựa trên giá trị. Ví dụ, giả sử bạn tạo một công cụ chọn màu có nhiều chế độ: bảng màu hoặc bánh xe màu RGB. Giá trị có thể gửi là màu được chọn ở dạng chuẩn, chẳng hạn như "#7fff00". Để khôi phục quyền điều khiển về trạng thái cụ thể, bạn cũng cần biết quyền điều khiển đang ở chế độ nào, do đó trạng thái có thể trông giống như "palette/#7fff00".
this.internals_.setFormValue(this.value_,
this.mode_ + '/' + this.value_);
Mã của bạn sẽ cần khôi phục trạng thái dựa trên giá trị trạng thái đã lưu trữ.
formStateRestoreCallback(state, mode) {
if (mode == 'restore') {
// expects a state parameter in the form 'controlMode/value'
[controlMode, value] = state.split('/');
this.mode_ = controlMode;
this.value_ = value;
}
// Chrome doesn't handle autofill for form-associated custom elements.
// In the autofill case, you might need to handle a raw value.
}
Trong trường hợp điều khiển đơn giản hơn (ví dụ như nhập số), giá trị có thể đủ để khôi phục điều khiển về trạng thái trước đó. Nếu bạn bỏ qua state khi gọi setFormValue(), thì giá trị sẽ được truyền tới formStateRestoreCallback().
formStateRestoreCallback(state, mode) {
// Simple case, restore the saved value
this.value_ = state;
}
Phát hiện tính năng
Bạn có thể sử dụng tính năng phát hiện để xác định xem sự kiện formdata và các thành phần tùy chỉnh liên quan đến biểu mẫu có khả dụng hay không. Không có polyfill nào được phát hành cho cả hai tính năng này. Trong cả hai trường hợp, bạn có thể quay lại thêm một phần tử biểu mẫu ẩn để truyền giá trị của điều khiển vào biểu mẫu.
Nhiều tính năng nâng cao hơn của các phần tử tuỳ chỉnh được liên kết với biểu mẫu có thể khó hoặc không thể polyfill.
if ('FormDataEvent' in window) {
// formdata event is supported
}
if ('ElementInternals' in window &&
'setFormValue' in window.ElementInternals.prototype) {
// Form-associated custom elements are supported
}
Sự kiện formdata cung cấp cho bạn một giao diện để thêm dữ liệu biểu mẫu vào quy trình gửi mà không cần tạo một phần tử <input> ẩn. Với API phần tử tuỳ chỉnh được liên kết với biểu mẫu, bạn có thể cung cấp một bộ chức năng mới cho các chế độ kiểm soát biểu mẫu tuỳ chỉnh hoạt động như các chế độ kiểm soát biểu mẫu tích hợp.