[Web Component] using `part` to allow applying styling from outside the shadow DOM
Let's say we have a web component:
import { getProductById } from "../services/Menu.js";
import { addToCart } from "../services/Order.js";
export default class DetailsPage extends HTMLElement {
constructor() {
super();
this.root = this.attachShadow({ mode: "open" });
const template = document.getElementById("details-page-template");
const content = template.content.cloneNode(true);
const styles = document.createElement("style");
this.root.appendChild(content);
this.root.appendChild(styles);
async function loadCSS() {
const request = await fetch("/components/DetailsPage.css");
styles.textContent = await request.text();
}
loadCSS();
}
async renderData() {
if (this.dataset.productId) {
this.product = await getProductById(this.dataset.productId);
this.root.querySelector("h2").textContent = this.product.name;
this.root.querySelector("img").src = `/data/images/${this.product.image}`;
this.root.querySelector(".description").textContent =
this.product.description;
this.root.querySelector(
".price"
).textContent = `$ ${this.product.price.toFixed(2)} ea`;
this.root.querySelector("button").addEventListener("click", () => {
addToCart(this.product.id);
app.router.go("/order");
});
} else {
alert("Invalid Product ID");
}
}
connectedCallback() {
this.renderData();
}
}
customElements.define("details-page", DetailsPage);
The template for the web component:
<template id="details-page-template">
<header>
<a href="#" onclick="app.router.go('/'); event.preventDefault()"
>< Back</a
>
<h2></h2>
<a></a>
</header>
<img src="" />
<p class="description"></p>
<p class="price"></p>
<button>Add to cart</button>
</template>
Let's say we want to apply styling from outside to the img
of the Web component. We cannot do it directly due to web compoennt has shadow DOM.
But we can add part
attribute to enable this change
<template id="details-page-template">
<header>
<a href="#" onclick="app.router.go('/'); event.preventDefault()"
>< Back</a
>
<h2></h2>
<a></a>
</header>
<!--add part to open shadow DOM to outside, so css can access it-->
<img src="" part="image" />
<p class="description"></p>
<p class="price"></p>
<button>Add to cart</button>
</template>
Now we can target the img
by using css:
details-page::part(image) {
margin-left: -10%;
width: 120%;
}