Skip to content

Instantly share code, notes, and snippets.

@calebdwilliams
Last active August 11, 2022 01:13
Show Gist options
  • Save calebdwilliams/c8c1e6c24effb05c1752ed3af61c58a2 to your computer and use it in GitHub Desktop.
Save calebdwilliams/c8c1e6c24effb05c1752ed3af61c58a2 to your computer and use it in GitHub Desktop.

Form-associated custom elements

Description

The form-associated custom elements APIs enable custom elements to participate in form submission and validation lifecycles.

Links

Status

Initial API Summary/Quick API Proposal

The form-associated custom elements APIs are implemented within the attachInternals method on the HTMLElement prototype. Calling attachInternals returns an instance of an ElementInternals object which grants developers the ability to interact with form elements provided they designate their element as a form-associated element.

<form>
  <fancy-input name="fancy"></fancy-input>
</form>

<script>
  class FancyInput extends HTMLElement {
    static get formAssociated() {
      return true;
    }
  
    constructor() {
      super();
      this.#internals = this.attachInternals();
      this.#internals.setFormValue('I can participate in a form!');
    }
  }

  customElements.define('fancy-input', FancyInput);
  
  document.querySelector('form').addEventListener('submit', event => {
    event.preventDefault();
    const formData = new FormData(event.target);
    
    console.log(formData.get('fancy')); // logs 'I can participate in a form!'
  });
</script>

The setFormValue method can accept several types of data including strings, FileData and FormData objects, the latter of which can allow a nested form to participate with a parent in its entirety.

In addition to providing an method for adding a value to a form object, the form-associated APIs provide a surface to allow custom elements to participate in form validation.

class FancyInput extends HTMLElement {
    static get formAssociated() {
      return true;
    }
  
    constructor() {
      super();
      const root = this.attachShadow({ mode: 'open' });
      this.#internals = this.attachInternals();
      this.#internals.setFormValue('I can participate in a form!');
      const button = document.createElement('button');
      root.append(button);
      
      button.addEventListener('click', this.#onClick);
      this.button = button;
    }
    
    #onClick = () => {
      if (this.#internals.invalid) {
        this.#internals.setValidity(); // Marks the element as valid
      } else {
        this.#internals.setValidity({
          customError: true
          }, 'You must click the button', this.button); // Marks the element as invalid and will focus on the button when the form checks validity
    }
  }

Key Scenarios

  • Enable custom elements to participate in form submission by adding a value to the FormData object, adding to the HTMLFormElement.prototype.elements object and in other submission formats.
  • Enables custom elements to participate in the form validation lifecycle by providing APIs similar to and building on top of the constraint validation APIs.

Concerns

Dissenting Opinion

Related Specs

Open Questions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment