我试图了解新的HTML自定义元素。
我的目标是,给定一些数据,创建自定义元素的n个实例。例如,给定10个用户的列表,创建10个用户html对象。
好的 - 所以我在html中定义了一个自定义元素
HTML
<template-user>
<div class="user-name"></div>
</template-user>
然后我创建我的控制器
JS
class UserTemplate extends HTMLElement {
constructor(){
super();
this.username = this.querySelectorAll('[class="user-name"]')[0];
}
setName(name){
this.username.innerHtml = name;
}
}
customElements.define('template-user', UserTemplate);
页面加载正常,但现在我对如何重用该元素感到困惑。如果我正在做正常的旧学校的东西,我会有一个for循环泵出一些HTML字符串并设置innerHTML的东西。但现在我宁愿做类似的事情
for(let i = 0; i < users.length; i++){
let userTemplate = new UserTemplate();
userTemplate.setName(user.name);
// append to user list, etc..
}
当我尝试这样做时,它似乎几乎可以工作。但它找不到username
,即this.querySelectorAll
将返回null。这只是在我尝试构造这个元素的新实例时。那怎么样,我应该创建新的自定义元素DOM对象?
确保您了解Web组件的构造函数的要求和限制:
https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-conformance
4.13.2自定义元素构造函数的要求
在创作自定义元素构造函数时,作者受以下一致性要求的约束:
在元素创建期间,直接或间接检查其中一些要求,如果不遵循它们,将导致无法通过解析器或DOM API实例化的自定义元素。即使工作在构造函数启动的微任务中完成,也是如此,因为微任务检查点可以在构造之后立即发生。
您可以进行类似于此的更改:
class TemplateUser extends HTMLElement {
static get observedAttributes() {
return ['user-name'];
}
constructor(){
super();
this.attachShadow({mode:'open'});
this.shadowRoot.innerHTML = '<div></div>';
}
attributeChangedCallback(attrName, oldVal, newVal) {
if (oldVal !== newVal) {
this.shadowRoot.firstChild.innerHTML = newVal;
}
}
get userName() {
return this.getAttribute('user-name');
}
set userName(name) {
this.setAttribute('user-name', name);
}
}
customElements.define('template-user', TemplateUser);
setTimeout( function () {
var el = document.querySelector('[user-name="Mummy"]');
el.userName = "Creature from the Black Lagoon";
}, 2000);
<template-user user-name="Frank N Stein"></template-user>
<template-user user-name="Dracula"></template-user>
<template-user user-name="Mummy"></template-user>
这使用shadowDOM存储<div>
,然后通过user-name
属性或通过userName
属性设置值。
但它无法找到用户名,即this.querySelectorAll将返回null。
当你创建一个新实例时,新元素没有子元素,所以querySelectorAll
将返回一个空的NodeList。如果查询DOM并选择已在标记中定义的template-user
,那么username
属性将引用您的div
元素。
如果您希望动态生成的template-user
元素默认具有<div class="user-name"></div>
子元素,则应在构造函数中创建并附加元素。
另外,为了选择第一个匹配元素,您可以使用.querySelector(...)
而不是.querySelectorAll(...)[0]
。
当您使用new
通过Javascript创建自定义元素时,您可以设置一些将作为constrcutor()
方法中的参数传递的变量:
for (let user of users) {
document.body.appendChild( new UserTemplate(user.name) )
}
您可以将其保存并保存在对象变量中,或者将其用作Shadow DOM中模板文字字符串中的变量。
class UserTemplate extends HTMLElement {
constructor(username){
super()
//this.username = username
this.attachShadow({ mode:'open' })
.innerHTML = `<div class="user-name"> ${username} </div>`
}
}
customElements.define('template-user', UserTemplate);
您可以在JS中创建组件并分配属性
let templateUser = document.createElement('template-user');
templateUser.userName= 'Your name here';
document.body.appendChild(templateUser);
或类似于根据您的需求的东西。 Google有一些文档here,大概是他们描述如何“在JavaScript中创建实例”的方式的2/3。这可能非常强大,特别是像你的例子中的for循环