Developers have long been eager to have more control over form elements. While this is a bit exaggerated, creating or customizing form components has been the holy grail of front-end web development for years.
Custom elements (e.g.<my-custom-element></my-custom-element>
) One of the most powerful features, has been quietly released since the Google Chrome 77 version and has been gradually applied to other browsers. The ElementInternals standard is a powerful set of features with a very low-key name. Its features include participating forms and APIs surrounding accessibility controls.
This article will explore how to create custom form controls, integrate constraint verification, introduce the basics of internal accessibility, and show how to combine these features to create highly portable macro form controls.
Let's start by creating a simple custom element that matches our design system. Our elements will save all styles in shadow DOM and ensure some basic accessibility. We will use the excellent LitElement library from the Google Polymer team as our code example, and while you absolutely don't need it, it does provide a good abstraction for writing custom elements.
In this example, we create a<rad-input></rad-input>
, it has some basic designs. We also added a second input to the form, which is a normal HTML input, and added the default value (so you can just press submit and see how it works).
When we click the Submit button, several things happen. First, the preventDefault
method of the commit event is called, in this case, to ensure that our page does not reload. After that, we create a FormData object that allows us to access information about the form, which we use to construct a JSON string and append it to<output></output>
element. However, note that the unique value added to our output comes from an element named "basic".
This is because our elements don't know how to interact with the form yet, so let's set up ours using the ElementInternals instance<rad-input></rad-input>
, to help it deserve its name. First, we need to call the attachInternals
method of our method in the element's constructor, and we will also import the ElementInternals polyfill into our page for use with browsers that do not yet support the specification.
The attachInternals
method returns a new element internal instance containing some new APIs that we can use in our methods. In order for our elements to take advantage of these APIs, we need to add a static formAssociated
getter that returns true
.
class RadInput extends LitElement { static get formAssociated() { return true; } constructor() { super(); this.internals = this.attachInternals(); } }
Let's take a look at some APIs in internals
attribute of an element:
-
setFormValue(value: string|FormData|File, state?: any): void
— This method sets the value of an element on its parent form if the parent form exists. If the value is null, the element will not participate in the form submission process. -
form
— If present, it is a reference to the parent form of the element. -
setValidity(flags: Partial<validitystate> , message?: string, anchor?: HTMLElement): void</validitystate>
— thesetValidity
method will help control the validity status of elements in the form. If the form is invalid, a verification message must exist. -
willValidate
— true if the element is evaluated when the form is submitted. -
validity
— A validity object that matches the API and semantics attached toHTMLInputElement.prototype.validity
. -
validationMessage
— If yousetValidity
to invalidate the control, this is the message describing the error. -
checkValidity
— Returns true if the element is valid, otherwise returns false and triggers an invalid event on the element. -
reportValidity
— Same ascheckValidity
, if the event is not cancelled, a problem is reported to the user. -
labels
— Uselabel[for]
attribute to mark the list of elements for this element. - Some other controls for setting aria information on elements.
Set the value of a custom element
Let's modify ours<rad-input></rad-input>
To take advantage of some of these APIs:
Here we modify the element's _onInput
method to contain a call to this.internals.setFormValue
. This tells the form that our element wants to register a value in the form using its given name (set as a property in our HTML). We also added a firstUpdated
method (similar to connectedCallback
when not using LitElement) which sets the element's value to an empty string when the element is finished rendering. This is to ensure that our elements always have the value of the form (although not required, you can exclude the elements from the form by passing in a null value).
Now when we add value to the input and submit the form we will be in our<output></output>
A radInput
value is seen in the element. We can also see that our element has been added to the radInput
property of HTMLFormElement
. However, one thing you may have noticed is that it still allows form submission despite our element having no value. Next, let's add some validation to our element.
Add constraint verification
To set the validation of the field, we need to modify the element a little to use the setValidity
method on the object inside the element. This method will accept three parameters (if the element is invalid, only the second parameter is required, and the third parameter is always optional). The first parameter is part of ValidityState
object. If any flag is set to true, the control will be marked as invalid. If one of the built-in validity keys does not meet your needs, you can use the common customError
key. Finally, if the control is valid, we will pass in an object literal ({}) to reset the control's validity.
The second parameter is the validity message of the control. This parameter is required if the control is invalid, and this parameter is not allowed if the control is valid. The third parameter is an optional verification target, which will control the user's focus if the form submission is invalid or if reportValidity
is called.
We will give our<rad-input></rad-input>
Introduce a new method to handle this logic:
_manageRequired() { const { value } = this; const input = this.shadowRoot.querySelector('input'); if (value === '' && this.required) { this.internals.setValidity({ valueMissing: true }, 'This field is required', input); } else { this.internals.setValidity({}); } }
This function gets the value and input of the control. If the value is equal to an empty string and the element is marked as required, we will call internals.setValidity
and toggle the validity of the control. Now all we need to do is call this method in our firstUpdated
and _onInput
methods, and we can add some basic validation to our elements.
In the absence of our<rad-input></rad-input>
Before entering the value, click the Submit button and the error message will now be displayed in browsers that support the ElementInternals specification. Unfortunately, polyfill still does not support displaying verification errors , as there is no reliable way to trigger the built-in verification popup in unsupported browsers.
We also added some basic accessibility information to our example by using our internals
object. We add an additional property _required
to the element, which will act as a proxy for this.required
and a getter/setter for required
.
get required() { return this._required; } set required(isRequired) { this._required = isRequired; this.internals.ariaRequired = isRequired; }
By passing the required
property to internals.ariaRequired
, we are reminding the screen reader that our element currently needs a value. In polyfill, this is done by adding the aria-required
attribute; however, in supported browsers, the attribute is not added to the element because the attribute is inherent to the element.
Create a micro form
Now that we have a valid input that fits our design system, we may want to start combining our elements into patterns that can be reused across multiple applications. One of the most eye-catching features of ElementInternals is that the setFormValue
method can take not only string and file data, but also FormData objects. So, suppose we want to create a common address form that might be used in multiple organizations, we can do this easily with our newly created elements.
In this example, we create a form within the shadow root of the element, in which we combine four<rad-input></rad-input>
Element to create an address form. Instead of calling setFormValue
with a string this time, we choose to pass the entire value of the form. As a result, our element passes the value of each individual element within its child form to the external form.
Adding constraint validation to this form would be a fairly simple process, as would providing additional styling, behavior, and content slots. Using these newer APIs ultimately allows developers to unlock a lot of potential in custom elements and ultimately gives us the freedom to control the user experience.
The above is the detailed content of Creating Custom Form Controls with ElementInternals. For more information, please follow other related articles on the PHP Chinese website!

This is the 3rd post in a small series we did on form accessibility. If you missed the second post, check out "Managing User Focus with :focus-visible". In

This tutorial demonstrates creating professional-looking JavaScript forms using the Smart Forms framework (note: no longer available). While the framework itself is unavailable, the principles and techniques remain relevant for other form builders.

The CSS box-shadow and outline properties gained theme.json support in WordPress 6.1. Let's look at a few examples of how it works in real themes, and what options we have to apply these styles to WordPress blocks and elements.

If you’ve recently started working with GraphQL, or reviewed its pros and cons, you’ve no doubt heard things like “GraphQL doesn’t support caching” or

The Svelte transition API provides a way to animate components when they enter or leave the document, including custom Svelte transitions.

This article explores the top PHP form builder scripts available on Envato Market, comparing their features, flexibility, and design. Before diving into specific options, let's understand what a PHP form builder is and why you'd use one. A PHP form

How much time do you spend designing the content presentation for your websites? When you write a new blog post or create a new page, are you thinking about

npm commands run various tasks for you, either as a one-off or a continuously running process for things like starting a server or compiling code.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

MinGW - Minimalist GNU for Windows
This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.

SAP NetWeaver Server Adapter for Eclipse
Integrate Eclipse with SAP NetWeaver application server.

MantisBT
Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)
