class Product extends HTMLElement {
connectedCallback() {
let products = [
{ name: "carrot", price: "$ 6.99" },
{ name: "blueberry", price: "$ 6.99" }
];
let pro = document.querySelector("#product_item").content;
let copyHTML = document.importNode(pro, true);
for (let product of products) {
copyHTML.querySelector(".cerealName").textContent = product.name;
this.attachShadow({ mode: "open" });
this.shadowRoot.append(copyHTML.cloneNode(true));
}
}
}
customElements.define("product-item", Product);
<main>
<product-item></product-item>
<product-item></product-item>
</main>
<template id="product_item">
<li >
<p style="font-size: 3rem"></p>
</li>
</template>
I expected the result to be a
carrot blueberry
But the result came with
carrot carrot
I can't seem to run the loop, can you tell me why?
CodePudding user response:
You only need to call attachShadow
once - after that it generates an error:
Failed to execute 'attachShadow' on 'Element': Shadow root cannot be created on a host which already hosts a shadow tree.
You also only need one <product-item>
element.
class Product extends HTMLElement {
connectedCallback() {
let products = [
{ name: "carrot", price: "$ 6.99" },
{ name: "blueberry", price: "$ 6.99" }
];
let pro = document.querySelector("#product_item").content;
let copyHTML = document.importNode(pro, true);
this.attachShadow({ mode: "open" });
for (let product of products) {
copyHTML.querySelector(".cerealName").textContent = product.name;
this.shadowRoot.append(copyHTML.cloneNode(true));
}
}
}
customElements.define("product-item", Product);
<main>
<product-item></product-item>
</main>
<template id="product_item">
<li >
<span style="font-size: 1.3rem"></span>
</li>
</template>
CodePudding user response:
And you are also cloning the <template>
twice with importNode
and cloneNode
cloneNode
is all you need, importNode
is for importing from other documents.
And your component processed all products
in one component; whilst you have multiple <product-item>
in hour HTML
So I presume you want to make <product-item>
display one product
I rewrote it a bit to (hopefully) let the code explain itself:
Note: the use of shadowDOM is probably overkill, it now blocks styling content.
Easily removed by removing 1 .attachShadow(...)
and 3 .shadowRoot
references.
customElements.define("product-item", class extends HTMLElement {
connectedCallback() {
let products = [{ name: "carrot", price: "6.99" },
{ name: "blueberry", price: "6.99" }];
let template = document.getElementById(this.nodeName).content;
this.attachShadow({mode:"open"}).append(template.cloneNode(true));
let product = products[this.getAttribute("nr")-1]; // UX starts at 1
if (product) {
this.shadowRoot.querySelector("b").textContent = product.name;
this.shadowRoot.querySelector("span").innerHTML = ` US$ ${product.price}`;
} else {
this.shadowRoot.innerHTML = `No product`
}
}
});
<ul>
<product-item nr=1></product-item>
<product-item nr=2></product-item>
<product-item nr=3></product-item>
</ul>
<template id="PRODUCT-ITEM">
<li >
<b>NAME</b><span>PRICE</span>
</li>
</template>
<style>
body {
color: red; /* "Inheritable" CSS styles not blocked! */
}
.product { /* blocked by shadowDOM */
color:green;
}
</style>
If you do want one <my-products>
creating all HTML in one go; innerHTML
is shorter and faster
customElements.define("my-products", class extends HTMLElement {
connectedCallback() {
let products = [{ name: "carrot", price: "$ 6.99" },
{ name: "blueberry", price: "$ 6.99" }];
this
//.attachShadow({mode:"open"})
.innerHTML = products.map(product =>
`<li ><b>${product.name}</b> - ${product.price}</li>`
).join("");
}
});
<my-products></my-products>
<style>
.product {
color:green;
}
</style>