Bardziej zaawansowane elementy sterujące formularzem

Dzięki nowemu zdarzeniu i interfejsom API elementów niestandardowych wypełnianie formularzy stało się znacznie łatwiejsze.

Arthur Evans

Wielu deweloperów tworzy niestandardowe elementy formularza, aby udostępnić elementy, których nie ma w przeglądarce, lub dostosować wygląd i wygląd w sposób wykraczający poza możliwości domyślnych elementów formularza.

Może być jednak trudno odtworzyć funkcje wbudowanych elementów sterujących formularza HTML. Oto niektóre funkcje, które element <input> otrzymuje automatycznie po dodaniu go do formularza:

  • Dane są automatycznie dodawane do listy elementów sterujących formularza.
  • Wartość pola jest automatycznie przesyłana wraz z formularzem.
  • Dane wejściowe są uwzględniane w weryfikacji formularza. Stylowanie pola wejściowego możesz określić za pomocą pseudoklas :valid:invalid.
  • Dane są przekazywane, gdy formularz jest resetowany, gdy jest ponownie wczytywany lub gdy przeglądarka próbuje automatycznie wypełnić pola formularza.

Elementy sterujące niestandardowych formularzy mają zwykle niewiele takich funkcji. Deweloperzy mogą obejść niektóre ograniczenia w JavaScript, np. dodać do formularza ukryty element <input>, aby umożliwić jego przesłanie. Innych funkcji nie można jednak odtworzyć tylko za pomocą JavaScript.

2 nowe funkcje internetowe ułatwiają tworzenie niestandardowych elementów sterujących formularzem i eliminują ograniczenia obecnych elementów sterujących:

  • Zdarzenie formdata umożliwia dowolnemu obiektowi JavaScriptu udział w przesyłaniu formularza, dzięki czemu możesz dodawać dane formularza bez użycia ukrytego obiektu <input>.
  • Interfejs API elementów niestandardowych powiązanych z formularzem umożliwia elementom niestandardowym działanie bardziej jak wbudowanym elementom sterującym formularza.

Te 2 funkcje można wykorzystać do tworzenia nowych rodzajów elementów sterujących, które działają lepiej.

Interfejs API oparty na zdarzeniach

Zdarzenie formdata to interfejs API niskiego poziomu, który umożliwia dowolnemu kodom JavaScript udział w przesyłaniu formularzy. Mechanizm działa w ten sposób:

  1. Do formularza, z którym chcesz wchodzić w interakcje, dodajesz odbiornik zdarzeń formdata.
  2. Gdy użytkownik kliknie przycisk przesyłania, formularz uruchamia zdarzenie formdata, które zawiera obiekt FormData zawierający wszystkie przesyłane dane.
  3. Każdy odbiorca zdarzenia formdata może dodać lub zmodyfikować dane przed przesłaniem formularza.

Oto przykład wysyłania pojedynczej wartości w obiekcie 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, korzystając z naszego przykładu na Glitch. Aby sprawdzić, jak działa interfejs API, uruchom go w Chrome 77 lub nowszej wersji.

Zgodność z przeglądarką

Obsługa przeglądarek

  • Chrome: 5.
  • Edge: 12.
  • Firefox: 4.
  • Safari: 5.

Źródło

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.

Standardowe elementy formularza uczestniczą w wielu częściach cyklu życia formularza oprócz jego przesyłania. Elementy niestandardowe powiązane z formularzami mają za zadanie wypełnić lukę między niestandardowymi widżetami a wbudowanymi elementami sterującymi. Elementy niestandardowe powiązane z formularzem mają wiele funkcji standardowych elementów formularzy:

  • Gdy umieścisz element niestandardowy powiązany z formularzem w elementach <form>, zostanie on automatycznie powiązany z formularzem, tak jak element kontrolny udostępniony przez przeglądarkę.
  • Element może być oznaczony 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 ma prawidłowy element wejściowy. Jeśli jedno z elementów sterujących formularza zawiera nieprawidłowe dane wejściowe, formularz nie zostanie przesłany.
  • Element może zapewniać wywołania zwrotne w różnych częściach cyklu życia formularza, np. gdy formularz jest wyłączony lub zresetowany do stanu domyślnego.
  • Element obsługuje standardowe pseudoklasy CSS dla elementów sterujących formularza, np. :disabled:invalid.

To mnóstwo funkcji. W tym artykule nie omawiamy wszystkich tych funkcji, ale opisujemy podstawy 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 stałą właściwość formAssociated do klasy elementu niestandardowego. Informuje przeglądarkę, że element ma być traktowany jak element sterujący formularzem.
  • Aby uzyskać dostęp do dodatkowych metod i właściwości elementów sterujących formularzem, takich jak setFormValue()setValidity(), wywołaj metodę attachInternals() elementu.
  • Dodaj typowe właściwości i metody obsługiwane przez elementy formularzy, takie jak name, valuevalidity.

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 kontrolek formularza udostępnianych 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 elementów sterujących formularzem. Najprostszą z nich jest metoda setFormValue(), która ustawia bieżącą wartość elementu sterującego.

Metoda setFormValue() może przyjmować 1 z 3 typów wartości:

  • Wartość ciągu znaków.
  • Obiekt File.
  • Obiekt FormData. Obiekt FormData może przekazywać wiele wartości (np. kontrolka wprowadzania danych 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, 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;

Element może też uczestniczyć w weryfikacji formularza przez wywołanie metody setValidity() obiektu 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_);
}

Element niestandardowy powiązany z formularzem możesz stylizować za pomocą pseudoklas :valid:invalid, tak jak wbudowany element 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. Zwróć uwagę, że funkcje zwrotne są opcjonalne: implementuj je tylko wtedy, gdy element musi coś wykonać w danym momencie cyklu życia.

void formAssociatedCallback(form)

Jest wywoływany, gdy przeglądarka powiąże element z elementem formularza lub odłączy go od elementu formularza.

void formDisabledCallback(disabled)

Wywoływany po zmianie stanu elementu disabled, gdy dodano lub usunięto atrybut disabled tego elementu albo gdy zmienił się stan disabled elementu <fieldset>, który jest przodkiem tego elementu. Parametr disabled reprezentuje nowy stan wyłączony elementu. Element może na przykład wyłączyć elementy w modelu DOM cienia, gdy sam zostanie wyłączony.

void formResetCallback()

Jest wywoływany po zresetowaniu formularza. Element powinien zresetować się do stanu domyślnego. W przypadku elementów <input> zwykle polega to na ustawieniu właściwości value tak, aby odpowiadała atrybucie value ustawionemu w znacznikach (lub w przypadku pola wyboru – tak, aby odpowiadała atrybucie checked).checked

void formStateRestoreCallback(state, mode)

Połączenie zostało odebrane w jednej z tych 2 sytuacji:

  • Gdy przeglądarka przywraca stan elementu (np. po przejściu do innej strony lub po ponownym uruchomieniu przeglądarki). W tym przypadku argument mode ma wartość "restore".
  • Gdy funkcje wspomagania wprowadzania danych w przeglądarce, np. autouzupełnianie formularzy, ustawiają wartość. W tym przypadku argument mode ma wartość "autocomplete".

Typ pierwszego argumentu zależy od sposobu wywołania metody setFormValue(). Więcej informacji znajdziesz w artykule Przywracanie stanu formularza.

Przywracanie stanu formularza

W niektórych okolicznościach, np. gdy użytkownik wróci na stronę lub uruchomi ponownie przeglądarkę, może ona spróbować przywrócić formularz do stanu, w którym był on przed zamknięciem.

W przypadku elementu niestandardowego powiązanego z formularzem przywrócone stan pochodzi z wartości przekazanych metodzie setFormValue(). Metodę możesz wywołać z jednym parametrem wartości, jak w wcześniejszych przykładach, lub z 2 parametrami:

this.internals_.setFormValue(value, state);

Wartość 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 może przyjmować te same typy co parametr value – może to być ciąg znaków, obiekt File lub FormData.

Parametr state jest przydatny, gdy nie możesz przywrócić stanu elementu sterującego na podstawie samej wartości. Załóżmy na przykład, że tworzysz selektor kolorów z kilkoma trybami: paletą lub kołem kolorów RGB. Przesyłana wartość to wybrany kolor w formie kanonicznej, np. "#7fff00". Aby jednak przywrócić kontroler do określonego stanu, musisz też wiedzieć, w jakim trybie był wcześniej. Stan może więc wyglądać tak: "palette/#7fff00".

this.internals_.setFormValue(this.value_,
    this.mode_ + '/' + this.value_);

Twój kod musiałby przywrócić 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. pola tekstowego z liczbą) wartość prawdopodobnie wystarczy do przywrócenia poprzedniego stanu. Jeśli pominiesz parametr state, gdy wywołujesz funkcję setFormValue(), jego wartość zostanie przekazana do parametru formStateRestoreCallback().

formStateRestoreCallback(state, mode) {
  // Simple case, restore the saved value
  this.value_ = state;
}

Przykład działającego kodu

W tym przykładzie połączono wiele funkcji elementów niestandardowych powiązanych z formularzem. Aby sprawdzić, jak działa interfejs API, uruchom go w Chrome 77 lub nowszej wersji.

Wykrywanie cech

Możesz użyć wykrywania funkcji, aby sprawdzić, czy zdarzenie formdata i elementy niestandardowe powiązane z formularzem są dostępne. Obecnie nie ma żadnych polyfilli dla żadnej z tych funkcji. W obu przypadkach możesz dodać ukryty element formularza, aby rozpowszechnić wartość elementu sterującego w formularzu. Wiele zaawansowanych funkcji elementów niestandardowych powiązanych z formularzami będzie prawdopodobnie trudnych lub niemożliwych do zastąpienia.

if ('FormDataEvent' in window) {
  // formdata event is supported
}

if ('ElementInternals' in window &&
    'setFormValue' in window.ElementInternals.prototype) {
  // Form-associated custom elements are supported
}

Podsumowanie

Elementy niestandardowe powiązane z zdarzeniem formdata i formularzem udostępniają nowe narzędzia do tworzenia niestandardowych elementów sterujących formularza.

Zdarzenie formdata nie zapewnia żadnych nowych funkcji, ale daje Ci interfejs do dodawania danych formularza 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 formularza, które działają jak wbudowane elementy formularza.

Baner powitalny autorstwa Oudom Pravat z Unsplash.