Opublikowano: 8 sierpnia 2019 r.
Wielu deweloperów tworzy niestandardowe elementy sterujące formularzem, aby udostępniać elementy, które nie są wbudowane w przeglądarkę, lub dostosowywać wygląd i charakter elementów w sposób, który nie jest możliwy w przypadku wbudowanych elementów sterujących formularzem.
Jednak odtworzenie funkcji wbudowanych kontrolek formularzy HTML może okazać się trudne. Rozważ niektóre funkcje, które element <input> otrzymuje automatycznie po dodaniu go do formularza:
- Wprowadzony tekst zostanie automatycznie dodany do listy elementów sterujących formularza.
- Wartość wprowadzona przez użytkownika jest automatycznie przesyłana wraz z formularzem.
- Pole bierze udział w weryfikacji formularza. Możesz nadać styl danym wejściowym przy użyciu pseudoklas
:validi:invalid. - Element wejściowy otrzymuje powiadomienie, gdy formularz zostanie zresetowany, ponownie załadowany lub gdy przeglądarka spróbuje automatycznie wypełnić pola formularza.
Niestandardowe kontrolki formularzy zazwyczaj mają niewiele z tych funkcji. Deweloperzy mogą obejść niektóre ograniczenia JavaScriptu, np. dodając do formularza ukryty element <input>, aby umożliwić przesyłanie formularza. Jednak innych funkcji nie da się odtworzyć wyłącznie w języku JavaScript.
Dwie funkcje internetowe ułatwiają tworzenie niestandardowych elementów sterujących formularza i usuwają ograniczenia niestandardowych elementów sterujących:
- Zdarzenie
formdataumożliwia dowolnemu obiektowi JavaScript udział w przesyłaniu formularza, dzięki czemu można dodawać dane formularza bez użycia ukrytego<input>. - Interfejs API niestandardowych elementów powiązanych z formularzem umożliwia elementom niestandardowym działanie w sposób przypominający wbudowane kontrolki formularza.
Obie te funkcje można wykorzystać do tworzenia nowych rodzajów lepiej działających elementów sterujących.
API oparte na zdarzeniach
Zdarzenie formdata to interfejs API niskiego poziomu, który umożliwia dowolnemu kodowi JavaScript udział w przesyłaniu formularza.
- Dodaj
formdatadetektor zdarzeń do formularza, z którym chcesz wejść w interakcję. - Gdy użytkownik kliknie przycisk „Wyślij”, formularz wyzwala zdarzenie
formdatazawierające obiektFormDataprzechowujący wszystkie przesyłane dane. - Każdy detektor
formdatama możliwość dodania lub zmodyfikowania danych przed przesłaniem formularza.
Oto przykład wysyłania pojedynczej wartości w 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);
});
Elementy niestandardowe powiązane z formularzem
Interfejs API oparty na zdarzeniach możesz stosować w przypadku dowolnego rodzaju komponentu, ale umożliwia on tylko interakcję z procesem przesyłania.
Standaryzowane kontrolki formularzy uczestniczą w wielu etapach cyklu życia formularza. Elementy niestandardowe powiązane z formularzem mają na celu wypełnienie luki między niestandardowymi widżetami a wbudowanymi elementami sterującymi. Niestandardowe elementy powiązane z formularzem mają wiele funkcji standardowych elementów formularza:
- Gdy umieścisz niestandardowy element powiązany z formularzem w
<form>, zostanie on automatycznie powiązany z formularzem, tak jak kontrolka dostarczona przez przeglądarkę. - Element można oznaczyć za pomocą elementu
<label>. - Element może ustawić wartość, która jest automatycznie przesyłana wraz z formularzem.
- Element może ustawić flagę wskazującą, czy dane wejściowe są prawidłowe. Jeśli któryś z elementów formularza zawiera nieprawidłowe dane wejściowe, formularza nie można wysłać.
- Element może zapewniać wywołania zwrotne dla różnych części cyklu życia formularza, na przykład gdy formularz jest wyłączany lub przywracany do stanu domyślnego.
- Element obsługuje standardowe pseudoklasy CSS dla elementów sterujących formularza, takie jak
:disabledi:invalid.
Ten dokument nie obejmuje wszystkich aspektów, ale opisuje podstawowe informacje potrzebne do zintegrowania elementu niestandardowego z formularzem.
Definiowanie elementu niestandardowego powiązanego z formularzem
Aby przekształcić element niestandardowy w element niestandardowy powiązany z formularzem, musisz wykonać kilka dodatkowych czynności:
- Dodaj statyczną właściwość
formAssociateddo klasy elementu niestandardowego. Informuje przeglądarkę, że element ma być traktowany jak kontrolka formularza. - Wywołaj metodę
attachInternals()na elemencie, 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,valueivalidity.
Oto jak te elementy pasują do podstawowej definicji elementu niestandardowego:
// 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);
Po zarejestrowaniu możesz używać tego elementu wszędzie tam, gdzie używasz kontrolki formularza udostępnionej 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 kontrolek formularza. Najbardziej podstawową z nich jest metoda
setFormValue(), która ustawia bieżącą wartość elementu sterującego.
Metoda setFormValue() może przyjmować jeden z trzech typów wartości:
- Wartość ciągu.
- Obiekt
File. - Obiekt
FormData. ObiektuFormDatamożna używać do przekazywania wielu wartości. Na przykład, kontrolka wprowadzania danych karty kredytowej może przesyłać numer karty, datę ważności i kod weryfikacyjny.
Aby ustawić wartość:
this.internals_.setFormValue(this.value_);
Aby ustawić wiele wartości, możesz zrobić coś takiego:
// 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
Kontrolka może również uczestniczyć w walidacji formularza poprzez wywołanie metody setValidity() na obiekcie internals.
// 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_);
}
Możesz nadać styl niestandardowemu elementowi powiązanemu z formularzem za pomocą pseudoklas :valid i :invalid, tak jak wbudowanemu kontrolkowi formularza.
Wywołania zwrotne cyklu życia
Interfejs API niestandardowych elementów powiązanych z formularzem obejmuje zestaw dodatkowych wywołań zwrotnych cyklu życia, które łączą się z cyklem życia formularza. Funkcje zwrotne są opcjonalne: należy zaimplementować funkcję zwrotną tylko wtedy, gdy element musi coś zrobić w danym momencie cyklu życia.
void formAssociatedCallback(form)
Wywoływane, gdy przeglądarka kojarzy element z elementem formularza lub oddziela element od elementu formularza.
void formDisabledCallback(disabled)
Wywoływane po zmianie disabled stanu elementu, na przykład z powodu dodania lub usunięcia atrybutu disabled tego elementu albo zmiany stanu disabled w <fieldset>, który jest przodkiem tego elementu.
Element może na przykład wyłączać elementy w swoim shadow DOM, gdy zostanie wyłączony.
void formResetCallback()
Wywoływane po zresetowaniu formularza. Element powinien zresetować się do pewnego rodzaju stanu domyślnego. W przypadku elementów <input> zwykle wiąże się to z ustawieniem właściwości value tak, aby odpowiadała atrybutowi value ustawionemu w znacznikach. W przypadku pola wyboru jest to związane z ustawieniem właściwości checked tak, aby odpowiadała atrybutowi checked.
void formStateRestoreCallback(state, mode)
Wywoływany w jednej z dwóch sytuacji:
- Gdy przeglądarka przywraca stan elementu, np. po nawigacji lub po ponownym uruchomieniu przeglądarki. Argument
modewynosi"restore". - Gdy funkcja przeglądarki wspomagająca wprowadzanie danych, np. automatyczne wypełnianie formularzy, ustawia wartość. Argument
modewynosi"autocomplete".
Typ pierwszego argumentu zależy od sposobu wywołania metody setFormValue().
Przywróć stan formularza
W pewnych okolicznościach, na przykład podczas powrotu na stronę lub ponownego uruchomienia przeglądarki, przeglądarka może próbować przywrócić formularz do stanu, w jakim użytkownik go zostawił.
W przypadku niestandardowego elementu powiązanego z formularzem przywrócony stan pochodzi z wartości przekazanych do metody setFormValue(). Możesz wywołać metodę z jednym parametrem wartości, jak pokazano w poprzednich przykładach, lub z 2 parametrami:
this.internals_.setFormValue(value, state);
value reprezentuje wartość kontrolki, którą można przesłać. Opcjonalny parametr state to wewnętrzna reprezentacja stanu kontrolki, która może obejmować dane, które nie są wysyłane do serwera. Parametr state przyjmuje te same typy co parametr value: obiekt ciągu, File lub FormData.
Parametr state jest przydatny, gdy nie można przywrócić stanu kontrolki wyłącznie na podstawie wartości. 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 wybrany kolor w formie kanonicznej, np. "#7fff00". Aby przywrócić sterowanie do określonego stanu, musisz też wiedzieć, w jakim trybie było ono wcześniej, więc stan 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 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 liczb) wartość ta prawdopodobnie wystarczy, aby przywrócić element sterujący do poprzedniego stanu. Jeśli pominiesz state podczas wywoływania setFormValue(), wartość zostanie przekazana do formStateRestoreCallback().
formStateRestoreCallback(state, mode) {
// Simple case, restore the saved value
this.value_ = state;
}
Wykrywanie cech
Za pomocą wykrywania funkcji możesz sprawdzić, czy zdarzenie formdata i elementy niestandardowe powiązane z formularzem są dostępne. Nie wydano żadnych polyfillów dla żadnej z tych funkcji. W obu przypadkach możesz powrócić do dodania ukrytego elementu formularza, aby rozpowszechnić wartość kontrolki w formularzu.
Wiele bardziej zaawansowanych funkcji niestandardowych elementów powiązanych z formularzem jest prawdopodobnie trudnych lub niemożliwych do uzupełnienia.
if ('FormDataEvent' in window) {
// formdata event is supported
}
if ('ElementInternals' in window &&
'setFormValue' in window.ElementInternals.prototype) {
// Form-associated custom elements are supported
}
Zdarzenie formdata udostępnia interfejs do dodawania danych z formularza do procesu przesyłania bez konieczności tworzenia ukrytego elementu <input>. Za pomocą interfejsu API elementów niestandardowych powiązanych z formularzem możesz udostępnić nowy zestaw funkcji dla niestandardowych elementów sterujących formularza, które działają jak wbudowane elementy sterujące formularza.