Table of Contents
Set the value of a custom element
Add constraint verification
Create a micro form
Home Web Front-end CSS Tutorial Creating Custom Form Controls with ElementInternals

Creating Custom Form Controls with ElementInternals

Mar 26, 2025 am 09:44 AM

Creating Custom Form Controls with ElementInternals

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();
  }
}
Copy after login

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> — the setValidity 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 to HTMLInputElement.prototype.validity .
  • validationMessage — If you setValidity 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 as checkValidity , if the event is not cancelled, a problem is reported to the user.
  • labels — Use label[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({});
  }
}
Copy after login

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;
}
Copy after login

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!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Hot Topics

Java Tutorial
1652
14
PHP Tutorial
1251
29
C# Tutorial
1224
24
Stacked Cards with Sticky Positioning and a Dash of Sass Stacked Cards with Sticky Positioning and a Dash of Sass Apr 03, 2025 am 10:30 AM

The other day, I spotted this particularly lovely bit from Corey Ginnivan’s website where a collection of cards stack on top of one another as you scroll.

Google Fonts   Variable Fonts Google Fonts Variable Fonts Apr 09, 2025 am 10:42 AM

I see Google Fonts rolled out a new design (Tweet). Compared to the last big redesign, this feels much more iterative. I can barely tell the difference

How to Create an Animated Countdown Timer With HTML, CSS and JavaScript How to Create an Animated Countdown Timer With HTML, CSS and JavaScript Apr 11, 2025 am 11:29 AM

Have you ever needed a countdown timer on a project? For something like that, it might be natural to reach for a plugin, but it’s actually a lot more

HTML Data Attributes Guide HTML Data Attributes Guide Apr 11, 2025 am 11:50 AM

Everything you ever wanted to know about data attributes in HTML, CSS, and JavaScript.

Why are the purple slashed areas in the Flex layout mistakenly considered 'overflow space'? Why are the purple slashed areas in the Flex layout mistakenly considered 'overflow space'? Apr 05, 2025 pm 05:51 PM

Questions about purple slash areas in Flex layouts When using Flex layouts, you may encounter some confusing phenomena, such as in the developer tools (d...

How to select a child element with the first class name item through CSS? How to select a child element with the first class name item through CSS? Apr 05, 2025 pm 11:24 PM

When the number of elements is not fixed, how to select the first child element of the specified class name through CSS. When processing HTML structure, you often encounter different elements...

A Proof of Concept for Making Sass Faster A Proof of Concept for Making Sass Faster Apr 16, 2025 am 10:38 AM

At the start of a new project, Sass compilation happens in the blink of an eye. This feels great, especially when it’s paired with Browsersync, which reloads

See all articles