自动填充/自动完成输入Web组件shadowDom



TL;DR:当输入处于阴影DOM中时,浏览器自动填充无法按预期工作,尤其是在使用Web组件时。

澄清:这篇文章的主题是带有自定义Web组件输入的HTML自动完成属性。这不是指自动完成搜索词。

设置:首先,假设您想要创建一个普通的HTML表单来收集用户的姓名、地址和电话号码。您将为每个数据点创建一个带有嵌套输入元素的表单元素和一个提交按钮。直截了当,这里没有什么异常。

现在,为了改善用户的体验,您为每个输入添加了autocomplete属性及其相关值。我相信你以前见过并使用过这个浏览器支持的功能,如果你和我一样,在填写地址、信用卡和用户名/密码的在线表格时,这是一种预期的便利。

到目前为止,我们还没有遇到任何问题——一切都在按预期进行。在输入中添加了自动完成属性后,浏览器会识别出您正在尝试填写表格,而典型的浏览器(如Chrome(会使用存储在浏览器中的任何用户提供的数据来帮助自动完成输入。在我们的情况下,如果您的Chrome偏好设置/自动填充/"地址等"中存储了信息,您将获得一个弹出列表,其中包含您存储的地址配置文件,用于填充表单。

扭曲:如果您将本机输入更改为具有开放shadowDom的Web组件——因为您可能想要一个具有一些验证和样式的可重用输入——则自动完成将不再有效。

预期结果:我希望浏览器自动完成功能能像往常一样工作,例如查找、关联和预填充输入,并且不会区分我们在shadowDom中的web组件输入。

这是一个已知的、缺乏的功能,目前正在开发中

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

保持最新。

您可以通过在web组件之外创建输入(和标签(并通过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
};
}

您可以使用::slotted伪选择器设置输入和标签的样式。

css`
::slotted(.enterCodeLabel) {}
::slotted(.enterCodeInput) {}
::slotted(.enterCodeInput:focus) {}
`

最新更新