1

TL;DR: Browser Autofill doesn't work as expected when inputs are in shadow DOMs, particularly noticed with the use of Web Components.

Clarification: The subject of this post is the HTML autocomplete attribute with a custom Web Component input. This is NOT referring to auto-completion of search terms.

Set up: First, let's suppose you want to create a vanilla HTML form to gather a user's name, address, and phone number. You would create a form element with a nested input element for each data point and a submit button. Straightforward and nothing unusual here.

Now, to improve the experience for your users you add the autocomplete attribute to each input with its associated value. I am sure you have seen and used this browser-supported feature before, and if you are like me, it is an expected convenience when filling out online forms for address, credit cards, and username/password.

Up to this point, we don't have any issues--everything is working as expected. With the autocomplete attributes added to the inputs, the browser recognizes that you are trying to fill out a form and a typical browser, such as Chrome, will use whatever user-provided data stored within the browser it can to help auto complete the inputs. In our case, granted you have information stored in your Chrome Preferences/Autofill/'Address and more', you will be given a pop-up list with your stored Address profiles to use to populate the form.

The Twist: If you change your native input to a Web Component with an open shadowDom--because perhaps you want a reusable input that has some validation and styling--the autocomplete no longer works.

Expected result: I would expect the browser autocomplete feature to work as it normally does, such as, find, associate, and prefill inputs and not discriminate web component inputs that our in shadowDoms.

Will Mangimelli
  • 150
  • 1
  • 7

2 Answers2

3

This is a known, lacking feature which is currently being worked on.

Follow https://groups.google.com/a/chromium.org/g/blink-dev/c/RY9leYMu5hI?pli=1 and https://bugs.chromium.org/p/chromium/issues/detail?id=649162

to stay up to date.

connexo
  • 53,704
  • 14
  • 91
  • 128
0

You can work around this by creating your input (and label) outside of the web component and including it via a slot.

const createInput = () => {
    const input = document.createElement('input');
    input.slot = 'input';
    input.className = 'enterCodeInput';
    input.name = 'code';
    input.id = 'code';
    input.autocomplete = 'one-time-code';
    input.autocapitalize = 'none';
    input.inputMode = 'numeric';
    return input;
};

const createLabel = () => {
    const label = document.createElement('label');
    label.htmlFor = 'code';
    label.className = 'enterCodeLabel';
    label.innerHTML = `Enter Code`;
    return label;
};

@customElement('foo')
class Foo extends LitElement {
    @state()
    protected _inputEl = createInput();

    @state()
    protected _labelEl = createLabel();
    
    public connectedCallback() {
        this._inputEl.addEventListener('input', this._handleCodeChange);
        this.insertAdjacentElement('beforeend', this._labelEl);
        this.insertAdjacentElement('beforeend', this._inputEl);
    }
    
    public disconnectedCallback() {
        this._inputEl?.removeEventListener('input', this._handleCodeChange);
        this._labelEl?.remove();
        this._inputEl?.remove();
    }
    
    public render() {
        return html`<form>
            <slot name="label"></slot>
            <slot name="input"></slot>
        </form>`;
    }
    
    protected _handleCodeChange = (e: Event) => {
        // Do something
    };
}

You can style the input and label using the ::slotted pseudo-selector.

css`
    ::slotted(.enterCodeLabel) {}
    ::slotted(.enterCodeInput) {}
    ::slotted(.enterCodeInput:focus) {}
`
drewloomer
  • 93
  • 2
  • 4
  • Thanks for this outline. Note that render() will not be executed unless super.connectedCallback() is included in connectedCallback(). – Simeon Bartley Oct 24 '22 at 01:58