Avec un nouvel événement et des API d'éléments personnalisés, il est désormais beaucoup plus facile de participer aux formulaires.
De nombreux développeurs créent des commandes de formulaire personnalisées, soit pour fournir des commandes qui ne sont pas intégrées au navigateur, soit pour personnaliser l'apparence au-delà de ce qui est possible avec les commandes de formulaire intégrées.
Cependant, il peut être difficile de répliquer les fonctionnalités des commandes de formulaire HTML intégrées. Voici quelques-unes des caractéristiques auxquelles un élément <input>
est automatiquement ajouté lorsque vous l'ajoutez à un formulaire:
- L'entrée est automatiquement ajoutée à la liste des commandes du formulaire.
- La valeur saisie est automatiquement envoyée avec le formulaire.
- L'entrée participe à la validation du formulaire. Vous pouvez styliser l'entrée à l'aide des pseudo-classes
:valid
et:invalid
. - L'entrée est avertie lorsque le formulaire est réinitialisé, lorsqu'il est actualisé ou que le navigateur tente de remplir automatiquement les entrées du formulaire.
Les commandes de formulaire personnalisées offrent généralement peu de fonctionnalités. Les développeurs peuvent contourner certaines limites de JavaScript, par exemple ajouter un <input>
masqué à un formulaire pour participer à son envoi. Mais d’autres fonctions ne peuvent tout simplement
pas être répliquées en JavaScript seul.
Deux nouvelles fonctionnalités Web facilitent la création de commandes de formulaire personnalisées et suppriment les limites des commandes personnalisées actuelles:
- L'événement
formdata
permet à un objet JavaScript arbitraire de participer à l'envoi du formulaire. Vous pouvez ainsi ajouter des données de formulaire sans utiliser de<input>
masqué. - L'API d'éléments personnalisés associés à Form permet aux éléments personnalisés de se comporter davantage comme des commandes de formulaire intégrées.
Ces deux fonctionnalités peuvent être utilisées pour créer de nouveaux types de commandes qui fonctionnent mieux.
API basée sur des événements
L'événement formdata
est une API de bas niveau qui permet à n'importe quel code JavaScript de participer à l'envoi de formulaires. Le mécanisme fonctionne comme suit:
- Vous devez ajouter un écouteur d'événements
formdata
au formulaire avec lequel vous souhaitez interagir. - Lorsqu'un utilisateur clique sur le bouton d'envoi, le formulaire déclenche un événement
formdata
, qui inclut un objetFormData
contenant toutes les données envoyées. - Chaque écouteur
formdata
a la possibilité d'ajouter ou de modifier les données avant l'envoi du formulaire.
Voici un exemple d'envoi d'une seule valeur dans un écouteur d'événements 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);
});
Essayez ceci en utilisant notre exemple sur Glitch. Veillez à l'exécuter sur Chrome 77 ou version ultérieure pour voir l'API en action.
Compatibilité du navigateur
Éléments personnalisés associés au formulaire
Vous pouvez utiliser l'API basée sur des événements avec n'importe quel type de composant, mais elle vous permet seulement d'interagir avec le processus d'envoi.
Les commandes de formulaire standardisées participent à de nombreuses parties du cycle de vie des formulaires, en plus de l'envoi. Les éléments personnalisés associés à un formulaire visent à combler l'écart entre les widgets personnalisés et les commandes intégrées. Les éléments personnalisés associés au formulaire correspondent à de nombreuses caractéristiques des éléments standardisés:
- Lorsque vous placez un élément personnalisé associé à un formulaire dans un élément
<form>
, il est automatiquement associé au formulaire, comme une commande fournie par le navigateur. - L'élément peut être étiqueté à l'aide d'un élément
<label>
. - L'élément peut définir une valeur qui est automatiquement envoyée avec le formulaire.
- L'élément peut définir un indicateur indiquant s'il contient ou non une entrée valide. Si l'un des contrôles du formulaire comporte une entrée non valide, le formulaire ne peut pas être envoyé.
- L'élément peut fournir des rappels pour différentes parties du cycle de vie du formulaire, par exemple lorsque le formulaire est désactivé ou réinitialisé à son état par défaut.
- L'élément est compatible avec les pseudo-classes CSS standards pour les commandes de formulaire, comme
:disabled
et:invalid
.
Cela représente beaucoup de fonctionnalités ! Cet article n'aborde pas tous ces points, mais décrit les principes de base nécessaires pour intégrer votre élément personnalisé dans un formulaire.
Définir un élément personnalisé associé à un formulaire
Pour transformer un élément personnalisé en élément personnalisé associé à un formulaire, vous devez suivre quelques étapes supplémentaires:
- Ajoutez une propriété
formAssociated
statique à votre classe d'élément personnalisé. Cela indique au navigateur de traiter l'élément comme une commande de formulaire. - Appelez la méthode
attachInternals()
sur l'élément pour accéder à des méthodes et propriétés supplémentaires pour les commandes de formulaire, commesetFormValue()
etsetValidity()
. - Ajoutez les propriétés et méthodes courantes compatibles avec les commandes de formulaire, telles que
name
,value
etvalidity
.
Voici comment ces éléments s'intègrent dans une définition d'élément personnalisé de base:
// 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);
Une fois l'élément enregistré, vous pouvez l'utiliser partout où vous utiliseriez une commande de formulaire fournie par le navigateur:
<form>
<label>Number of bunnies: <my-counter></my-counter></label>
<button type="submit">Submit</button>
</form>
Définir une valeur
La méthode attachInternals()
renvoie un objet ElementInternals
qui permet d'accéder aux API de contrôle de formulaire. La plus basique est la méthode setFormValue()
, qui définit la valeur actuelle de la commande.
La méthode setFormValue()
peut utiliser l'un des trois types de valeurs suivants:
- Valeur de chaîne.
- Un objet
File
. - Un objet
FormData
. Vous pouvez utiliser un objetFormData
pour transmettre plusieurs valeurs (par exemple, une commande de saisie de carte de crédit peut transmettre un numéro de carte, une date d'expiration et un code de validation).
Pour définir une valeur simple:
this.internals_.setFormValue(this.value_);
Pour définir plusieurs valeurs, procédez comme suit:
// 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);
Validation des entrées
Votre commande peut également participer à la validation du formulaire en appelant la méthode setValidity()
sur l'objet "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_);
}
Vous pouvez styliser un élément personnalisé associé à un formulaire avec les pseudo-classes :valid
et :invalid
, tout comme une commande de formulaire intégrée.
Rappels de cycle de vie
Une API d'élément personnalisé associé à un formulaire inclut un ensemble de rappels de cycle de vie supplémentaires à associer au cycle de vie du formulaire. Les rappels sont facultatifs: n'implémentez un rappel que si votre élément doit effectuer une action à ce stade du cycle de vie.
void formAssociatedCallback(form)
Appelé lorsque le navigateur associe l'élément à un élément de formulaire ou le dissocie d'un élément du formulaire.
void formDisabledCallback(disabled)
Appelé après que l'état disabled
de l'élément change, soit parce que l'attribut disabled
de cet élément a été ajouté ou supprimé, soit parce que l'état disabled
a changé sur un <fieldset>
qui est un ancêtre de cet élément. Le paramètre disabled
représente le nouvel état désactivé de l'élément. L'élément peut, par exemple, désactiver des éléments dans son Shadow DOM lorsqu'il est désactivé.
void formResetCallback()
Appelé après la réinitialisation du formulaire. L'élément devrait se réinitialiser à une sorte d'état par défaut. Pour les éléments <input>
, cela implique généralement de définir la propriété value
de sorte qu'elle corresponde à l'attribut value
défini dans le balisage (ou, dans le cas d'une case à cocher, de définir la propriété checked
pour qu'elle corresponde à l'attribut checked
).
void formStateRestoreCallback(state, mode)
Appelé dans l'une des deux situations suivantes:
- Lorsque le navigateur restaure l'état de l'élément (par exemple, après une navigation ou au redémarrage du navigateur). Dans ce cas, l'argument
mode
est"restore"
. - Lorsque les fonctionnalités d'aide à la saisie du navigateur, telles que le remplissage automatique de formulaires, définissent une valeur. Dans ce cas, l'argument
mode
est"autocomplete"
.
Le type du premier argument dépend de la manière dont la méthode setFormValue()
a été appelée. Pour en savoir plus, consultez la section Restaurer l'état du formulaire.
Restauration de l'état du formulaire
Dans certains cas, par exemple lorsque vous revenez sur une page ou redémarrez le navigateur, il peut essayer de restaurer le formulaire dans l'état dans lequel il l'avait laissé.
Pour un élément personnalisé associé à un formulaire, l'état restauré provient des valeurs que vous transmettez à la méthode setFormValue()
. Vous pouvez appeler la méthode avec un seul paramètre de valeur, comme indiqué dans les exemples précédents, ou avec deux paramètres:
this.internals_.setFormValue(value, state);
L'élément value
représente la valeur de contrôle pouvant être envoyée. Le paramètre facultatif state
est une représentation interne de l'état de la commande, qui peut inclure des données qui ne sont pas envoyées au serveur. Le paramètre state
utilise les mêmes types que le paramètre value
. Il peut s'agir d'une chaîne, ou d'un objet File
ou FormData
.
Le paramètre state
est utile lorsque vous ne pouvez pas restaurer l'état d'une commande en fonction de la seule valeur. Par exemple, supposons que vous créiez un sélecteur de couleur avec plusieurs modes: palette ou roue chromatique RVB. La valeur à envoyer correspond à la couleur sélectionnée dans un format canonique, tel que "#7fff00"
. Toutefois, pour restaurer un état spécifique, vous devez également savoir dans quel mode il se trouvait, de sorte que l'state puisse ressembler à "palette/#7fff00"
.
this.internals_.setFormValue(this.value_,
this.mode_ + '/' + this.value_);
Votre code doit restaurer son état en fonction de la valeur d'état stockée.
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.
}
Dans le cas d'une commande plus simple (par exemple, la saisie d'un nombre), la valeur est probablement suffisante pour restaurer la commande à son état précédent. Si vous omettez state
lors de l'appel de setFormValue()
, la valeur est transmise à formStateRestoreCallback()
.
formStateRestoreCallback(state, mode) {
// Simple case, restore the saved value
this.value_ = state;
}
Exemple pratique
L'exemple suivant rassemble de nombreuses caractéristiques des éléments personnalisés associés au formulaire. Veillez à l'exécuter sur Chrome 77 ou version ultérieure pour voir l'API en action.
Détection de caractéristiques
Vous pouvez utiliser la détection de fonctionnalités pour déterminer si l'événement formdata
et les éléments personnalisés associés au formulaire sont disponibles. Aucun polyfill n'est actuellement publié pour ces fonctionnalités. Dans les deux cas, vous pouvez ajouter un élément de formulaire masqué pour propager la valeur de la commande dans le formulaire. Bon nombre des fonctionnalités plus avancées des éléments personnalisés associés à un formulaire seront probablement difficiles, voire impossibles, à émuler.
if ('FormDataEvent' in window) {
// formdata event is supported
}
if ('ElementInternals' in window &&
'setFormValue' in window.ElementInternals.prototype) {
// Form-associated custom elements are supported
}
Conclusion
L'événement formdata
et les éléments personnalisés associés au formulaire fournissent de nouveaux outils pour créer des commandes de formulaire personnalisées.
L'événement formdata
n'offre aucune nouvelle fonctionnalité, mais offre une interface vous permettant d'ajouter les données de votre formulaire au processus d'envoi, sans avoir à créer d'élément <input>
masqué.
L'API d'éléments personnalisés associés au formulaire fournit un nouvel ensemble de fonctionnalités permettant de créer des commandes de formulaire personnalisées qui fonctionnent comme des commandes de formulaire intégrées.
Image principale par Oudom Pravat sur Unsplash.