在 web component 中动态添加元素
Dynamically add elements inside web component
我想创建一个 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>
...
在你的情况下,你不能在 Shadow DOM shadowRoot
属性 上使用 insertAjacentHTML()
因为 Shadow Root 没有实现 Element 接口。
使用绑定( this )
更好的解决方案是在 shadowRoot
属性 上使用 appendChild(
)。但是,您需要在 click
事件回调中添加一个特殊的 bind()
操作。
在事件回调中,this 确实引用了触发事件的元素,而不是定义回调的对象。
为了获取对自定义元素的引用(为了访问 Shadow DOM shadowRoot
中的输入元素,在 addEventListener()
中调用 bind(this)
。
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>
扩展 Supersharps 答案:
attachShadow()
设置 this.shadowRoot
默认
attachShadow()
returns shadowRoot,所以你可以在其上链接 .innerHTML
appendChild()
returns 附加的 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>
感谢 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);
我想创建一个 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>
...
在你的情况下,你不能在 Shadow DOM shadowRoot
属性 上使用 insertAjacentHTML()
因为 Shadow Root 没有实现 Element 接口。
使用绑定( this )
更好的解决方案是在 shadowRoot
属性 上使用 appendChild(
)。但是,您需要在 click
事件回调中添加一个特殊的 bind()
操作。
在事件回调中,this 确实引用了触发事件的元素,而不是定义回调的对象。
为了获取对自定义元素的引用(为了访问 Shadow DOM shadowRoot
中的输入元素,在 addEventListener()
中调用 bind(this)
。
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>
扩展 Supersharps 答案:
attachShadow()
设置this.shadowRoot
默认attachShadow()
returns shadowRoot,所以你可以在其上链接.innerHTML
appendChild()
returns 附加的 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>
感谢 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);