Search code examples
custom-element

reportValidity() failure with form-associated customElement


I have a form-associated customElement which manages its own validity and validation messages. It works as expected as a member of a form; calling form.reportValidity() will place the appropriate error message on the appropriate anchor element. However, calling customElement.reportValidity() results in the error: An invalid form control with name='' is not focusable.

My understanding of reportValidity() is that we can call it on individual elements, whether it is a member of an HTMLFormControlsCollection or not. For example, you can call report validity on the following orphan input:

document.querySelector("#foo").reportValidity();
<input id="foo" value="" required>

So because the above works, I don't believe I'm out of line with any design principle.

My minimal repro sets up a form associted custom element by calling attachInternals and building a shadowDom of a single input element. A single instance of this custom element is nested within a form element. The page loads, and calls reportValidity on both the form and the custom element. The call on the custom element fails:

class FACE extends HTMLElement {
    static get formAssociated() { return true; }
    constructor() {
        super();
        this.attachShadow({mode:"open"});
        this._internals = this.attachInternals();        
    }

    connectedCallback() {
        this.shadowRoot.innerHTML = `<input id="foo" value="initial">`;
        let errorAnchor = this.shadowRoot.querySelector("#foo");
        this._internals.setValidity({badInput: true}, "Some error message", errorAnchor);
    }

    reportValidity() { // expose reportValidity on the CE's surface
        return this._internals.reportValidity();
    }
}

customElements.define("fa-ce", FACE);
customElements.whenDefined("fa-ce").then(
    () => {
        // reports custom validation message
        document.querySelector("form").reportValidity();
        // "An invalid form control with name='' is not focusable."
        document.querySelector("fa-ce").reportValidity();
    }
);
<form>
    <fa-ce></fa-ce>
</form>

So (most probable) I'm doing something wrong and you might could help, or this is a bug in (Chrome's) custom elements (FF hasn't implemented attachInternals yet), or this is as designed and what I'm trying to do can't be done.


Solution

  • update There now is a Bug reported:

    https://bugs.chromium.org/p/chromium/issues/detail?id=1139621&q=%22not%20focusable%22&can=2

    This answer might no longer be valid.


    You need to make the Form Element inside shadowDOM focusable
    when you create the shadowDOM:

    this.attachShadow({
            mode: "open",
            delegatesFocus: true
          });
    

    Does not seem to work in a SO snippet...

    working JSFiddle at: https://jsfiddle.net/WebComponents/9uq15ndr/

    .

    a delegateFocus explanation JSFiddle at: https://jsfiddle.net/WebComponents/n7mrkxz6

    as you said: This does not work in FireFox yet (jan 2020)

    works in Chrome, Edge, Safari (last one I did not test myself)

    Another cause of the error can be a hidden required Field, which the browser can't focus on

    <input type="hidden" required />