我已经构建了一个简单的 2 个文本字段自定义元素,但无法进行验证以正常工作。没有涵盖这方面的教程。
这是我的自定义元素:
/* name.js */
class MyName extends HTMLElement {
static formAssociated = true
constructor() {
super()
this.internals = this.attachInternals()
this.internals.formAssociated = true;
var shadowRoot = this.attachShadow({mode: 'open', delegatesFocus: true})
this.data = new FormData()
this.data.set('firstname', '')
this.data.set('lastname', '')
shadowRoot.innerHTML = this.html
}
connectedCallback() {
// any DOM manipulation goes here, run only if not initialised
if (!this.initialized) {
console.log('initialised');
this.onUpdateValue();
this.shadowRoot.querySelectorAll('input').forEach( el => el.addEventListener('input', event => {
console.log('content changed')
event.target.classList.add('show_error');
this.data.set(event.target.name, event.target.value)
this.internals.setFormValue(this.data)
this.onUpdateValue(); // trigger the form validation
this.internals.setValidity({ customError: true }, 'data not valid');
}));
// waits before displaying the element to prevent flickering
setTimeout(() => this.shadowRoot.querySelector('div').classList.remove('hidden'), 100);
}
}
onUpdateValue() {
// The Constraint Validation API
let valid = true;
const p = this.shadowRoot.querySelector('p.error');
this.shadowRoot.querySelectorAll('input').forEach(element => {
if (element.validity.valid === false) valid = false;
})
if (valid) {
this.internals.setValidity({});
} else {
console.log('invalid');
this.internals.setValidity({ customError: true }, 'data not valid');
}
}
get willValidate() { return this.internals.willValidate }
checkValidity() { return this.internals.checkValidity() }
reportValidity() {return this.internals.reportValidity() }
get value() {
return this.data
}
set value(v) {
if (v instanceof FormData) {
this.shadowRoot.querySelector('input[name="firstname"]').value = v.get('firstname')
this.shadowRoot.querySelector('input[name="lastname"]').value = v.get('lastname')
this.data = v
this.internals.setFormValue(v)
}
}
formResetCallback() {
this.data.set('firstname', '')
this.data.set('lastname', '')
this.shadowRoot.querySelector('input[name="firstname"]').value = this.data.get('firstname')
this.shadowRoot.querySelector('input[name="lastname"]').value = this.data.get('lastname')
}
formDisabledCallback(isDisabled) {
this.shadowRoot.querySelector('input[name="firstname"]').disabled = isDisabled
this.shadowRoot.querySelector('input[name="lastname"]').disabled = isDisabled
}
reportValidity() { // expose reportValidity on the CE's surface
return this._internals.reportValidity();
}
static get observedAttributes() {
return ['value'];
}
html = `
<link rel="stylesheet" href="common.css" />
<style>
// removes any inherited css
#container {
all: initial;
}
// prevents the flicker as the style is applied
.hidden {
visibility: hidden;
// display: none;
}
input:invalid {
border-color: red;
}
</style>
<div class="hidden">
<h2 part="title">Address Form</h2>
<p><input type="text" name="firstname" placeholder="firstname" minlength="5" /></p>
<p><input type="text" name="lastname" placeholder="lastname" minlength="5" /></p>
<p class="error"></p>
</div>
`
}
customElements.define("my-name", MyName)
我有一个简单的页面,其中包含此自定义元素(name =“foobar”)以及一个提交按钮,其中以下事件触发表单提交:
document.querySelector('form').addEventListener('submit', event => {
event.preventDefault();
console.log(document.querySelector('my-name').checkValidity());
// getting the value of the custom element
const name = document.querySelector('my-name').value;
document.querySelector('pre#formdata').textContent = JSON.stringify(Object.fromEntries(name.entries()), null, 2);
})
当我加载页面并单击按钮时,控制台中记录了以下错误:
名称='foobar'的无效表单控件不可聚焦。
我想我明白发生了什么,浏览器正在尝试突出显示自定义元素(但失败了)。我假设这是每个构建这些 FACE 元素的人都面临的问题,但找不到发布的任何解决方案。
任何帮助将不胜感激...
我在同一条船上。我不知道所有的答案,但我已经差不多了。我还没有让错误消息消失,但已经让元素聚焦并显示错误消息弹出窗口。
您缺少的主要内容是将内部输入元素作为第三个参数包含到
this.internals.setValidity
。这准确地告诉它焦点在哪里。
在组件上暴露
validity
getter 也很好。如果您在表单元素上调用 checkValidity
或 reportValidity
,浏览器将使用它。如果您有其他方式显示它们,组件的使用者可能希望在表单上设置 novalidate
并在提交时手动调用 checkValidity
以跳过这些本机浏览器弹出错误消息。
您也许能够从 Material 3 的 Web 组件逆向工程出更完整的解决方案。特别要注意的是它们的 mixins 中的表单关联元素和验证,以及各个组件如何在这些 mixins 上实现抽象方法。 复选框几乎没有其他考虑因素。
我从Material comment中发现了 setValidity 调用,内容如下:
当表单尝试提交或调用
delegatesFocus: true
时,<input>
到form.reportValidity()
的元素将在 Chrome 和 Safari 中引发错误:“名称 ='' 的无效表单控件不可聚焦”。有效性锚点必须在internals.setValidity()
中提供,并且必须是渲染的<input>
元素。