我想创建一个Web组件,其中包含可以添加到其中的元素列表。 例如,如果我有一个初始模板,例如:
const template = document.createElement("template");
template.innerHTML = `<input type="text"></input><button>add div</button>`;
class MyElement extends HTMLElement {
constructor() {
super();
this._shadowRoot = this.attachShadow({ mode: "open" });
this._shadowRoot.appendChild(template.content.cloneNode(true));
const button = this._shadowRoot.querySelector("button");
button.addEventListener("click", this.addDiv);
}
addDiv(e) {
// ...
}
}
customElements.define("my-element", MyElement);
每单击一次按钮,就会添加一个<div>
,其中包含来自输入字段的文本,从而创建如下内容:
<input type="text"></input><button>add div</button>
<div>first text from input added</div>
<div>second text from input added</div>
...
答案 0 :(得分:1)
在您的情况下,您不能在Shadow DOM insertAjacentHTML()
属性上使用shadowRoot
,因为Shadow Root无法实现Element接口。
更好的解决方案是在appendChild(
属性上使用shadowRoot
)。但是,您需要在click
事件回调中添加特殊的bind()
操作。
在事件回调中,它确实引用触发事件的元素,而不是在其中定义回调的对象。
为了获得对自定义元素的引用(为了访问Shadow DOM shadowRoot
中的输入元素,请在bind(this)
内调用addEventListener()
。
button.addEventListener( "click", this.addDiv.bind( this ) )
请参阅下面的完整示例:
const template = document.createElement("template");
template.innerHTML = `<input type="text"></input><button>add div</button>`;
class MyElement extends HTMLElement {
constructor() {
super();
this._shadowRoot = this.attachShadow({ mode: "open" });
this._shadowRoot.appendChild(template.content.cloneNode(true));
const button = this._shadowRoot.querySelector("button");
button.addEventListener("click", this.addDiv.bind( this ) );
}
addDiv(e) {
var div = document.createElement( 'div' )
div.textContent = this.shadowRoot.querySelector( 'input' ).value
this.shadowRoot.appendChild( div )
}
}
customElements.define("my-element", MyElement);
<my-element></my-element>
另一个解决方案是使用arrow function。使用箭头功能,此功能不会重新定义,因此您无需使用bind()
。
class MyElement extends HTMLElement {
constructor() {
super()
const sh = this.attachShadow( { mode: "open" } )
sh.innerHTML = `<input type="text"></input><button>add div</button>`
const button = sh.querySelector( "button" )
button.onclick = ev => {
let div = document.createElement( "div" )
div.textContent = sh.querySelector( "input" ).value
sh.appendChild( div )
}
}
}
customElements.define( "my-element", MyElement )
<my-element></my-element>
答案 1 :(得分:1)
扩展到Supersharps答案:
attachShadow()
设置 this.shadowRoot
attachShadow()
返回 shadowRoot,因此您可以在其上链接.innerHTML
appendChild()
返回附加的DIV,因此您可以对其进行链接
class MyElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"})
.innerHTML = '<input type="text"></input><button>add div</button>';
this.shadowRoot.querySelector('button').onclick = evt =>
this.shadowRoot
.appendChild(document.createElement("div"))
.innerHTML = this.shadowRoot.querySelector("input").value
}
}
customElements.define("my-element", MyElement)
<my-element></my-element>
或将其重写为一个辅助函数$ append
这使得其余代码可读性
class MyElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
let $append = ( tag, html = '' ) => (
tag = this.shadowRoot.appendChild(document.createElement(tag)),
tag.innerHTML = html,
tag // return tag, so onclick can be chained
);
let input = $append('input');
$append('button', 'add div').onclick = evt => $append("div", input.value);
}
}
customElements.define("my-element", MyElement)
<my-element></my-element>
答案 2 :(得分:0)
由于Danny的回答,我意识到我不需要创建this._shadowRoot
,因此我的问题和Supersharp的回答可以简化为以下内容。我保留模板是因为从模板创建Web组件是一种好习惯,因为与使用shadowRoot.innerHTML
相比,性能要好。
const template = document.createElement("template");
template.innerHTML = `<input type="text"></input><button>add div</button>`;
class MyElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(template.content.cloneNode(true));
const button = this.shadowRoot.querySelector("button");
button.addEventListener("click", this.addDiv.bind(this));
}
addDiv(e) {
const div = document.createElement("div");
div.textContent = this.shadowRoot.querySelector("input").value;
this.shadowRoot.appendChild(div);
}
}
customElements.define("my-element", MyElement);