Tutorials on Ui Components

Learn about Ui Components from fellow newline community members!

  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL
  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL

Form-associated custom elements FTW!

Before Shadow DOM, you needed a framework to encapsulate component templates or styling. Shadow DOM was a game-changer because it allows you to code UI components without their logic clashing with other components using just the web platform. Shadow DOM poses challenges when HTML elements encapsulated by Shadow DOM need to participate in a form. In this post, I'll provide an overview of form-associated custom elements. Form-associated custom elements is a web specification that allows engineers to code custom form controls that report value and validity to HTMLFormElement , while also promoting a fully accessible user experience. With the encapsulation provided by Shadow DOM, engineers can code UI components where the CSS styling doesn't collide with other components. Shadow DOM provides a DOM tree for an element separated from the rest of the Document Object Model (DOM). The separation of concerns promoted by Shadow DOM is a boon for coding reusable UI components. While Shadow DOM has several benefits, there are some complications when elements embedded in Shadow DOM have to interact with HTMLFormElement . Suppose you wanted to code a custom checkbox component using Shadow DOM. Checkboxes usually require a significant amount of CSS styling that overrides the browser defaults to match a given mockup. You code an autonomous custom element and style the HTMLInputElement with type="checkbox" in the context of Shadow DOM so the styling doesn't conflict with other elements. You give the component a tag name of my-checkbox . Just when you think you're following best practices, you place an instance of the custom element as a child of HTMLFormElement . Upon inspection in Dev Tools, you may notice the HTMLInputElement cannot participate with the form. You can inspect this phenomenon in this CodeSandbox . HTMLInputElement by design can report value and validity back to HTMLFormElement , but only when HTMLInputElement is a direct descendent of HTMLFormElement . When coding reusable components it's a good idea to provide an interface for web engineers that's familiar. It's typical for HTMLInputElement that are direct descendants of HTMLFormElement to have access to the parent form directly on the element. You can inspect this behavior in the following CodeSandbox . Since the HTMLInputElement is found in an entirely different DOM tree (Shadow DOM), the HTMLFormElement doesn't recognize the HTMLInputElement . In short, HTMLInputElement embedded in Shadow DOM can't participate in the form. In 2019, a new specification was proposed that solves this issue. Form-associated custom elements allow web engineers to use the benefits of Shadow DOM while providing an API that enables custom elements to participate in HTMLFormElement . Form-associated custom elements have all the benefits of autonomous custom elements. They can implement Shadow DOM and use the typical custom lifecycle hooks because they inherit from HTMLElement . If you've coded autonomous custom elements, learning how to code form-associated custom elements is fairly similar. In the following examples, I'll demonstrate how a checkbox embedded in Shadow DOM can participate in an HTML form by implementing formAssociated and ElementInternals . Before I mentioned that form controls that are native to the browser like HTMLInputElement automatically participate in HTMLFormElement . The form control is added to an Array-like interface on HTMLFormElement , allowing web engineers to loop through the form controls to handle common tasks like validation. For the Checkbox component to participate in HTMLFormElement the same way, you simply need to set the value of a static property named formAssociated to true . the below example written with TypeScript does just that. If you wish to follow along, fork this CodeSandbox and start coding. Inversely, if you wish to reference HTMLFormElement on instances of Checkbox , similar to how HTMLInputElement behaves when it's a direct descendant of HTMLFormElement , a method inherited from HTMLElement called attachInternals can be called with provides the same interface, along with the Accessibility Object Model (AOM). By setting a property on Checkbox named _internals to what's returned by attachInternals , you effectively add the ElementInternals interface to Checkbox . Later in this post, I'll provide an example of how you can reference a method on the ElementInternals interface that aids with validation. Before that, we should resolve some discrepancies between Checkbox and a typical HTMLInputElement . If we expect engineers to reuse this component, it should behave similarly to HTMLInputElement , which has a well-known interface. To provide parity between HTMLInputElement and Checkbox , let's define some getters and setters on Checkbox . First, make a getter that returns a reference of the HTMLInputElement so you can easily reference the element with this.checkbox throughout the logic of the component. Next, define a getter and setter for the state of the checkbox. It's probably a good idea to make the HTMLInputElement the single source of truth here. Any getter and setter defined on Checkbox either returns the value or sets the value of checked on this.checkbox . We could introduce several more properties on Checkbox to provide parity between it and a typical HTMLInputElement , but we'll stop there for now. While coding a UI library filled with form-associated custom elements, I found a couple of challenges in making the components reusable. Suppose you wanted to add validation logic to Checkbox and have the class another method called onValidate , including all the logic there. In the below example, I call setValidity on the ElementInternals interface, which reports the validity of the input to HTMLFormElement . This is convenient, however placing this logic here doesn't give a web engineer the ability to configure validations per business logic in different scenarios. A higher-level validation pattern is required that would allow engineers to loop through form controls and validate an entire form. Another challenge had to do with making inline validation messages accessible. Getting screen readers to interpret validation messages as errors that should be read aloud at first seems tricky because of Shadow DOM, although is possible using WAI-ARIA attributes. Suppose this were the template instead of just the input. If the form control is invalid, custom logic could populate the <div class="message"> with relevant content. The WAI-ARIA attributes provide an immediate response for screen readers. Did you like what you read here? In the book Fullstack Web Components , you'll code a form-associated custom element, bringing all the features necessary to reuse that component in an enterprise-grade web application. You'll not just learn how to provide parity between the form control well-known elements like HTMLInputElement , but also discover a pattern for implementing reusable validations that validate an entire form. You'll also tackle challenges with making form-associated custom elements accessible. This is just in Chapter 3! Fullstack Web Components provides everything you need to know to code an entire UI library of custom elements. Are you looking to code Web Components now, but don't know where to get started? I wrote a book titled Fullstack Web Components:Complete Guide to Building UI Libraries with Web Components , a hands-on guide to coding UI libraries and web applications with custom elements. In Fullstack Web Components , you'll...

Thumbnail Image of Tutorial  Form-associated custom elements FTW!

I got a job offer, thanks in a big part to your teaching. They sent a test as part of the interview process, and this was a huge help to implement my own Node server.

This has been a really good investment!

Advance your career with newline Pro.

Only $30 per month for unlimited access to over 60+ books, guides and courses!

Learn More