Dzięki nowemu zdarzeniu i interfejsom API elementów niestandardowych korzystanie z formularzy stało się o wiele łatwiejsze.
Wielu programistów tworzy niestandardowe elementy sterujące, które nie są wbudowane w przeglądarkę, lub umożliwiają dostosowanie wyglądu i stylu strony za pomocą wbudowanych opcji.
Odtwarzanie funkcji wbudowanych elementów sterujących formularza HTML może być jednak trudne. Zastanów się nad niektórymi funkcjami, które element <input>
otrzymuje automatycznie po dodaniu do formularza:
- Dane wejściowe zostaną automatycznie dodane do listy elementów sterujących formularza.
- Zawartość formularza jest przesyłana automatycznie.
- Dane wejściowe są uwzględniane podczas weryfikacji formularza. Styl danych wejściowych możesz zmienić za pomocą pseudoklas
:valid
i:invalid
. - Pojawi się powiadomienie, gdy formularz zostanie zresetowany, ponownie wczytany lub gdy przeglądarka spróbuje automatycznie uzupełnić wpisy.
Niestandardowe elementy sterujące formularza zwykle mają niewiele z tych funkcji. Deweloperzy mogą obejść niektóre ograniczenia języka JavaScript, np. dodać do formularza ukrytego atrybutu <input>
, aby umożliwić jego przesłanie. Jednak innych funkcji po prostu nie da się powielać w samym kodzie JavaScript.
Dwie nowe funkcje internetowe ułatwiają tworzenie niestandardowych elementów sterujących formularza i usuwają ograniczenia dotychczasowych opcji:
- Zdarzenie
formdata
umożliwia dowolnemu obiektowi JavaScript udział w przesyłaniu formularza, dzięki czemu możesz dodawać dane do formularzy bez korzystania z ukrytego elementu<input>
. - Interfejs API elementów niestandardowych powiązanych z formularzem pozwala elementom niestandardowym w bardziej podobny sposób działać jak wbudowane elementy sterujące formularza.
Dzięki tym dwóm funkcjom można tworzyć nowe rodzaje ustawień, które działają lepiej.
Interfejs API oparty na zdarzeniach
Zdarzenie formdata
to interfejs API niskiego poziomu, który umożliwia udział dowolnego kodu JavaScript w przesłaniu formularza. Mechanizm działa w następujący sposób:
- Dodajesz detektor zdarzeń
formdata
do formularza, z którym chcesz wejść w interakcję. - Gdy użytkownik kliknie przycisk przesyłania, formularz wywoła zdarzenie
formdata
, które zawiera obiektFormData
przechowujący wszystkie przesyłane dane. - Każdy detektor
formdata
ma możliwość dodania lub zmodyfikowania danych przed przesłaniem formularza.
Oto przykład wysłania pojedynczej wartości za pomocą detektora 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 tę funkcję na przykładzie 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.
Poza przesłaniem formularza ustandaryzowane elementy sterujące formularza biorą udział w wielu częściach jego cyklu życia. Elementy niestandardowe powiązane z formularzem mają na celu wypełnienie luki między niestandardowymi widżetami a wbudowanymi elementami sterującymi. Elementy niestandardowe powiązane z formularzem pasują do wielu funkcji standardowych elementów formularzy:
- Gdy umieścisz element niestandardowy powiązany z formularzem w elemencie
<form>
, zostanie on automatycznie powiązany z formularzem, podobnie jak element sterujący obsługiwany przez przeglądarkę. - Element można oznaczyć etykietą za pomocą elementu
<label>
. - Element może ustawiać wartość, która jest automatycznie przesyłana z formularzem.
- Element może ustawić flagę wskazującą, czy zawiera prawidłowe dane wejściowe. Jeśli jedno z elementów sterujących formularza zawiera nieprawidłowe dane, nie można go przesłać.
- Ten element może udostępniać wywołania zwrotne różnych części cyklu życia formularza, np. gdy formularz jest wyłączony lub przywrócony do stanu domyślnego.
- Ten element obsługuje standardowe pseudoklasy CSS dla elementów sterujących formularza, takie jak
:disabled
i:invalid
.
Mnóstwo funkcji! W tym artykule nie omówimy wszystkich. Znajdziesz w nim jednak podstawowe informacje potrzebne do integracji elementu niestandardowego z formularzem.
Definiowanie elementu niestandardowego powiązanego z formularzem
Aby przekształcić element niestandardowy w element niestandardowy powiązany z formularzem, wykonaj kilka dodatkowych czynności:
- Dodaj statyczną właściwość
formAssociated
do klasy elementu niestandardowego. Dzięki temu przeglądarka będzie traktować element jak element sterujący formularza. - Wywołaj w elemencie metodę
attachInternals()
, aby uzyskać dostęp do dodatkowych metod i właściwości elementów sterujących formularza, takich jaksetFormValue()
isetValidity()
. - Dodaj typowe właściwości i metody obsługiwane przez elementy sterujące formularza, takie jak
name
,value
czyvalidity
.
Elementy te 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 sterowania formularzem udostępnianym przez przeglądarkę:
<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 sterowania formularzem. Najbardziej podstawowa z nich jest metoda setFormValue()
, która ustawia bieżącą wartość elementu sterującego.
Metoda setFormValue()
może mieć 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. funkcja wprowadzania danych karty kredytowej może przekazać numer karty, datę ważności i kod weryfikacyjny).
Aby ustawić prostą wartość:
this.internals_.setFormValue(this.value_);
Aby ustawić wiele wartości, wykonaj te 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
Grupa kontrolna 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 określić za pomocą pseudoklas :valid
i :invalid
(tak jak w przypadku wbudowanego elementu sterującego formularza).
Wywołania zwrotne cyklu życia
Interfejs API elementu niestandardowego powiązanego z formularzem zawiera zestaw dodatkowych wywołań zwrotnych cyklu życia, które można powiązać z cyklem życia formularza. Wywołania zwrotne są opcjonalne – korzystaj z nich tylko wtedy, gdy element musi wykonać jakąś czynność w danym momencie 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ływane po zmianie stanu elementu disabled
z powodu dodania lub usunięcia atrybutu disabled
tego elementu albo zmiany stanu disabled
elementu <fieldset>
, który jest elementem nadrzędnym tego elementu. Parametr disabled
wskazuje nowy stan wyłączonego elementu. Ten element może na przykład wyłączać elementy w modelu shadow DOM, gdy jest wyłączony.
void formResetCallback()
Wywoływana po zresetowaniu formularza. Element powinien się zresetować do jakiegoś stanu domyślnego. W przypadku elementów <input>
oznacza to zwykle ustawienie właściwości value
tak, aby pasowała do atrybutu value
określonego w znacznikach (lub w przypadku pola wyboru ustawienie właściwości checked
na wartość zgodną z atrybutem checked
).
void formStateRestoreCallback(state, mode)
Wywołano w jednym z 2 przyczyn:
- Gdy przeglądarka przywraca stan elementu (na przykład po nawigacji lub po ponownym uruchomieniu). W tym przypadku argument
mode
to"restore"
. - Gdy funkcje Asystenta wprowadzania w przeglądarce, 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 uruchomienia przeglądarki, przeglądarka może próbować przywrócić formularz do stanu, w którym użytkownik go zostawił.
Przywrócenie elementu niestandardowego powiązanego z formularzem pochodzi z wartości przekazywanych do metody setFormValue()
. Możesz wywołać tę metodę z jednym parametrem wartości, jak pokazano we wcześniejszych przykładach, lub za pomocą 2 parametrów:
this.internals_.setFormValue(value, state);
value
reprezentuje wartość kontroli, którą można przesłać. Opcjonalny parametr state
to wewnętrzny stan elementu sterującego, który może zawierać dane, które nie są wysyłane na serwer. Parametr state
przyjmuje te same typy co parametr value
– może to być ciąg znaków, obiekt File
lub obiekt 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. Atrybut value, który można przesłać, to wybrany kolor w formie kanonicznej, np. "#7fff00"
. Aby jednak przywrócić kontrolę do określonego stanu, musisz też wiedzieć, w jakim trybie to nastąpiło, więc state może wyglądać tak: "palette/#7fff00"
.
this.internals_.setFormValue(this.value_,
this.mode_ + '/' + this.value_);
Kod musiałby zostać przywrócony 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 (na przykład wprowadzania wartości) wartość prawdopodobnie wystarczy do przywrócenia go do poprzedniego stanu. Jeśli pominiesz właściwość state
przy wywoływaniu funkcji setFormValue()
, wartość zostanie przekazana do formStateRestoreCallback()
.
formStateRestoreCallback(state, mode) {
// Simple case, restore the saved value
this.value_ = state;
}
Praktyczny przykład
Poniższy przykład stanowi połączenie wielu funkcji elementów niestandardowych powiązanych z formularzem. Aby zobaczyć, jak działa ten interfejs API, uruchom go w Chrome w wersji 77 lub nowszej.
Wykrywanie funkcji
Za pomocą wykrywania funkcji możesz określić, czy dostępne są zdarzenie formdata
i elementy niestandardowe powiązane z formularzem. Obecnie nie jest dostępny żaden kod polyfill dla żadnej z tych funkcji. W obu przypadkach możesz wrócić do dodawania ukrytego elementu formularza, aby przekazać wartość elementu sterującego do formularza. Wprowadzenie wielu zaawansowanych funkcji elementów niestandardowych powiązanych z formularzem będzie prawdopodobnie trudne lub niemożliwe do wykorzystania polyfill.
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 elementy niestandardowe powiązane z formularzem zapewniają nowe narzędzia do tworzenia niestandardowych elementów sterujących.
Zdarzenie formdata
nie daje żadnych nowych możliwości, ale udostępnia interfejs umożliwiający dodawanie danych formularzy do procesu przesyłania bez konieczności tworzenia ukrytego elementu <input>
.
Interfejs API elementów niestandardowych powiązanych z formularzem udostępnia nowy zestaw funkcji do tworzenia niestandardowych elementów sterujących, które działają jak wbudowane elementy sterujące formularza.
Baner powitalny autorstwa Oudom Pravat w filmie Unsplash.