Dzięki nowemu zdarzeniu i interfejsom API elementów niestandardowych znacznie ułatwiliśmy wypełnianie formularzy.
Wielu deweloperów tworzy niestandardowe elementy sterujące formularza, aby umożliwiać korzystanie z elementów sterujących, które nie są wbudowane w przeglądarkę, lub aby dostosować wygląd i styl ponad to, co jest możliwe dzięki wbudowanym elementom sterującym.
Odtwarzanie funkcji wbudowanych elementów sterujących formularza HTML może być jednak trudne. Weź pod uwagę niektóre funkcje, które element <input>
otrzymuje automatycznie po dodaniu go do formularza:
- To pole zostanie automatycznie dodane do listy elementów sterujących formularza.
- Wartość wejściowa jest automatycznie przesyłana razem z formularzem.
- Dane wejściowe są uwzględniane podczas weryfikacji formularza. Możesz określić styl danych wejściowych, używając pseudoklas
:valid
i:invalid
. - Dane wejściowe są powiadamiane, gdy formularz zostanie zresetowany, odświeżony lub przeglądarka spróbuje automatycznie uzupełnić wpisy w formularzu.
Elementy sterujące formularza niestandardowego zwykle mają niewiele z tych funkcji. Deweloperzy mogą obejść niektóre ograniczenia występujące w kodzie JavaScriptu, takie jak dodanie do formularza ukrytego <input>
, aby umożliwić przesyłanie formularzy. Innych funkcji nie da się jednak powielić w samym JavaScripcie.
Dwie nowe funkcje internetowe ułatwiają tworzenie niestandardowych elementów sterujących formularza i eliminują ograniczenia tych elementów:
- Zdarzenie
formdata
umożliwia dowolnemu obiektowi JavaScriptu uczestniczenie w przesyłaniu formularza, więc możesz dodawać dane do formularza bez używania ukrytego obiektu<input>
. - Interfejs API elementów niestandardowych związanych z formularzem pozwala tym elementom działać bardziej jak wbudowane elementy sterujące formularza.
Dzięki tym 2 funkcjom można tworzyć nowe, lepiej działające ustawienia.
Interfejs API oparty na zdarzeniach
Zdarzenie formdata
to niskopoziomowy interfejs API, który umożliwia udział dowolnego kodu JavaScript w przesyłaniu formularza. Mechanizm działa tak:
- Dodaj detektor zdarzeń
formdata
do formularza, z którym chcesz korzystać. - Gdy użytkownik kliknie przycisk przesyłania, formularz wywoła zdarzenie
formdata
, które zawiera obiektFormData
, w którym znajdują się wszystkie przesyłane dane. - Każdy detektor
formdata
ma możliwość dodania lub zmiany danych przed przesłaniem formularza.
Oto przykład wysyłania pojedynczej wartości w detektorze zdarzeń 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);
});
Wypróbuj to na przykładzie w Glitch. Aby zobaczyć, jak działa ten interfejs API, uruchom go w Chrome w wersji 77 lub nowszej.
Zgodność z przeglądarką
Elementy niestandardowe powiązane z formularzem
Interfejsu API opartego na zdarzeniach możesz używać z dowolnym komponentem, ale umożliwia on tylko interakcję z procesem przesyłania.
Ustandaryzowane elementy sterujące formularza są uwzględniane w wielu częściach cyklu życia formularza poza jego przesyłaniem. Elementy niestandardowe związane z formularzem mają wypełnić lukę między niestandardowymi widżetami a wbudowanymi elementami sterującymi. Powiązane z formularzem elementy niestandardowe pasują do wielu funkcji standardowych elementów formularzy:
- Gdy umieścisz w elemencie
<form>
element niestandardowy powiązany z formularzem, zostanie on automatycznie powiązany z formularzem, tak jak w przypadku elementu sterującego w przeglądarce. - Element może być oznaczony etykietą za pomocą elementu
<label>
. - Element może ustawić wartość, która jest przesyłana automatycznie w formularzu.
- Element może ustawić flagę wskazującą, czy zawiera on prawidłowe dane wejściowe. Jeśli jeden z elementów sterujących formularza zawiera nieprawidłowe dane wejściowe, nie można przesłać formularza.
- Ten element może zawierać wywołania zwrotne do różnych części cyklu życia formularza – na przykład gdy formularz jest wyłączony lub przywrócony do stanu domyślnego.
- Element obsługuje standardowe pseudoklasy CSS na potrzeby elementów sterujących formularza, np.
:disabled
i:invalid
.
To mnóstwo funkcji! W tym artykule nie omówimy wszystkich z nich, ale opiszemy podstawowe kwestie potrzebne do zintegrowania elementu niestandardowego z formularzem.
Definiowanie elementu niestandardowego powiązanego z formularzem
Aby przekształcić element niestandardowy w niestandardowy element powiązany z formularzem, trzeba wykonać kilka dodatkowych czynności:
- Dodaj do klasy elementu niestandardowego właściwość statyczną
formAssociated
. Dzięki temu przeglądarka będzie traktować ten element jako element sterujący formularza. - Wywołaj metodę
attachInternals()
w elemencie, aby uzyskać dostęp do dodatkowych metod i właściwości dla elementów sterujących formularza, takich jaksetFormValue()
czysetValidity()
. - Dodaj typowe właściwości i metody obsługiwane przez elementy sterujące formularza, takie jak
name
,value
ivalidity
.
Oto jak te elementy pasują do podstawowej definicji elementu niestandardowego:
// Form-associated custom elements must be autonomous custom elements--
// meaning 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);
Po zarejestrowaniu możesz używać tego elementu wszędzie tam, gdzie używasz ustawienia formularza w przeglądarce:
<form>
<label>Number of bunnies: <my-counter></my-counter></label>
<button type="submit">Submit</button>
</form>
Ustawianie wartości
Metoda attachInternals()
zwraca obiekt ElementInternals
, który zapewnia dostęp do interfejsów API kontroli formularza. Podstawowa z nich to metoda setFormValue()
, która ustawia bieżącą wartość elementu sterującego.
Metoda setFormValue()
może przyjmować jeden z 3 typów wartości:
- Wartość ciągu znaków.
- Obiekt
File
. - Obiekt
FormData
. Za pomocą obiektuFormData
możesz przekazywać wiele wartości (np. element sterujący danymi karty kredytowej może przekazywać numer karty, datę ważności i kod weryfikacyjny).
Aby ustawić prostą wartość:
this.internals_.setFormValue(this.value_);
Aby ustawić wiele wartości, wykonaj następujące czynności:
// 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);
Weryfikacja danych wejściowych
Element sterujący może też uczestniczyć w weryfikacji formularza, wywołując metodę setValidity()
w obiekcie wewnętrznym.
// 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_);
}
Styl elementu niestandardowego powiązanego z formularzem możesz zmieniać za pomocą pseudoklas :valid
i :invalid
, tak jak w przypadku wbudowanych elementów sterujących formularza.
Wywołania zwrotne cyklu życia
Interfejs API elementu niestandardowego powiązanego z formularzem zawiera zestaw dodatkowych wywołań zwrotnych cyklu życia, które są powiązane z cyklem życia formularza. Wywołania zwrotne są opcjonalne: zaimplementuj wywołanie zwrotne tylko wtedy, gdy element musi coś zrobić na tym etapie cyklu życia.
void formAssociatedCallback(form)
Wywoływane, gdy przeglądarka powiąże element z elementem formularza lub odłączy go od elementu formularza.
void formDisabledCallback(disabled)
Wywoływana po zmianie stanu disabled
elementu, ponieważ atrybut disabled
tego elementu został dodany lub usunięty albo stan disabled
zmienił się w elemencie <fieldset>
, który jest elementem nadrzędnym tego elementu. Parametr disabled
reprezentuje nowy stan wyłączenia elementu. Element może na przykład wyłączać elementy w swoim modelu shadow DOM, jeśli jest wyłączony.
void formResetCallback()
Wywoływane po zresetowaniu formularza. Element powinien się zresetować do jakiegoś stanu domyślnego. W przypadku elementów <input>
zazwyczaj wymaga to skonfigurowania właściwości value
tak, aby pasowała do atrybutu value
ustawionego w znacznikach (lub w przypadku pola wyboru – dostosowanie właściwości checked
do atrybutu checked
.
void formStateRestoreCallback(state, mode)
Wywołanie w jednej z dwóch sytuacji:
- Gdy przeglądarka przywróci stan elementu (np. po przejściu nawigacji lub ponownym uruchomieniu przeglądarki). W tym przypadku argument
mode
to"restore"
. - Gdy funkcje przeglądarki wspomagające dane wejściowe, takie jak autouzupełnianie formularzy, ustawiają wartość. W tym przypadku argument
mode
to"autocomplete"
.
Typ pierwszego argumentu zależy od sposobu wywołania metody setFormValue()
. Więcej informacji znajdziesz w artykule Przywracanie stanu formularza.
Przywracam stan formularza
W pewnych okolicznościach, np. podczas powrotu na stronę lub ponownego uruchamiania przeglądarki, przeglądarka może spróbować przywrócić formularz do stanu, w którym został pozostawiony przez użytkownika.
W przypadku elementu niestandardowego powiązanego z formularzem stan przywrócony zależy od wartości przesłanych do metody setFormValue()
. Metodę można wywołać z jednym parametrem wartości, jak pokazano we wcześniejszych przykładach, lub za pomocą dwóch parametrów:
this.internals_.setFormValue(value, state);
value
reprezentuje wartość elementu sterującego, którą można przesłać. Opcjonalny parametr state
to wewnętrzna reprezentacja stanu elementu sterującego, która może zawierać dane, które nie są wysyłane na serwer. Parametr state
ma te same typy co parametr value
– może być ciągiem znaków, File
lub obiektem FormData
.
Parametr state
jest przydatny, gdy na podstawie samej wartości nie można przywrócić stanu elementu sterującego. Załóżmy na przykład, że tworzysz selektor kolorów z wieloma trybami: paletą lub kołem kolorów RGB. Wartość, którą można przesłać, to kolor wybrany w postaci kanonicznej, np. "#7fff00"
. Aby jednak przywrócić kontrolkę do określonego stanu, musisz też wiedzieć, w którym trybie się ona znajdowała, więc state może wyglądać tak: "palette/#7fff00"
.
this.internals_.setFormValue(this.value_,
this.mode_ + '/' + this.value_);
Twój kod musiałby przywrócić swój stan na podstawie zapisanej wartości stanu.
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 currently doesn't handle autofill for form-associated
// custom elements. In the autofill case, you might need to handle
// a raw value.
}
W przypadku prostszego elementu sterującego (np. w postaci liczby wejściowej) wartość jest prawdopodobnie wystarczająca do przywrócenia elementu sterującego do poprzedniego stanu. Jeśli pominiesz state
podczas wywoływania funkcji setFormValue()
, wartość zostanie przekazana do formStateRestoreCallback()
.
formStateRestoreCallback(state, mode) {
// Simple case, restore the saved value
this.value_ = state;
}
Dobry przykład
Poniższy przykład łączy wiele funkcji elementów niestandardowych związanych z formularzem. Aby zobaczyć, jak działa ten interfejs API, uruchom go w Chrome w wersji 77 lub nowszej.
Wykrywanie cech
Wykrywanie funkcji pozwala określić, czy zdarzenie formdata
i elementy niestandardowe powiązane z formularzem są dostępne. Obecnie dla żadnej z tych funkcji nie są dostępne żadne zasoby polyfill. W obu przypadkach możesz wrócić do dodania ukrytego elementu formularza, aby przekazać wartość ustawienia do formularza. Wiele z bardziej zaawansowanych funkcji powiązanych z formularzem niestandardowych elementów może być trudne lub niemożliwe do wypełnienia.
if ('FormDataEvent' in window) {
// formdata event is supported
}
if ('ElementInternals' in window &&
'setFormValue' in window.ElementInternals.prototype) {
// Form-associated custom elements are supported
}
Podsumowanie
Zdarzenie formdata
i powiązane z formularzem elementy niestandardowe oferują nowe narzędzia do tworzenia niestandardowych elementów sterujących formularza.
Zdarzenie formdata
nie zapewnia żadnych nowych możliwości, ale zapewnia interfejs do dodawania danych formularza do procesu przesyłania bez konieczności tworzenia ukrytego elementu <input>
.
Interfejs API elementów niestandardowych związanych z formularzem zapewnia nowy zestaw możliwości tworzenia niestandardowych elementów sterujących formularza, które działają jak wbudowane elementy sterujące formularza.
Baner powitalny autorstwa Oudoma Pravata na kanale Unsplash.