I created a custom HTML element:
class PieChart extends HTMLElement {
constructor() {
super();
}
init() {
this.#render();
}
#render() {
const circleElement = document.createElement("circle");
circleElement.setAttribute("cx", 100);
circleElement.setAttribute("cy", 100);
circleElement.setAttribute("r", 80);
circleElement.setAttribute("fill", "none");
circleElement.setAttribute("stroke", "green");
circleElement.setAttribute("stroke-width", 40);
circleElement.setAttribute("stroke-dasharray", `502.85 502.85`);
const svgElement = document.createElement("svg");
svgElement.style.display = "block";
svgElement.style.width = "inherit";
svgElement.style.height = "inherit";
svgElement.setAttribute("viewBox", `0 0 200 200`);
svgElement.append(circleElement);
this.appendChild(svgElement);
this.style.display = "block";
this.style.width = "200px";
this.style.height = "200px";
}
connectedCallback() {
const event = new CustomEvent("init", {
bubbles: true
});
this.dispatchEvent(event);
}
}
customElements.define(
"pie-chart",
PieChart
);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<link rel="stylesheet" href="./css/site.css" />
</head>
<body>
<p>Hello!</p>
<pie-chart></pie-chart>
</body>
</html>
<script>
document.body.addEventListener("init", (pEvent) => {
const element = pEvent.target;
element.init(this);
});
</script>
This code is executed without any problems and the custom element is rendered in the DOM. However, the circle is not rendered on the screen.
PS: if I open DOM in a browser and click "Edit As HTML" on the PieChart element and change anything inside and save it, the circle appears. I used this pattern several times before and there were not problems, but this time is the first one when I use SVG element.
2
Answers
This is not really how you create a custom element. Create a shadow DOM in the connectedCallback, and the browser does the rest, including "activating" any custom elements that already exist on the page by the time you register their tags. No custom event dispatching, the custom element system was designed to take care of this for you.
And since you’re using SVG: remember that even though SVG is "part of HTML" ever since HTML5, SVG elements on the JS side still require the use of document.createElementNS with the SVG namespace. It’s super weird, super inconvenient, but we’re stuck with it.
Krusty, your replies in the comments are rude, very rude!.
Had your question not been about Web Components, I would have downvoted and ignored any of your questions or answers in the future.
Now I downvoted your question, and wrote this answer because others reading Stack Overflow should not be presented with (partially) incorrect information, because your were rude.
Have I said yet your behavior is very very very rude?
Now on to the answers.
Mike is very right when he says the way you
init
your Web Component is wrong.The
connectedCallback
executes when the Web Components enters the DOM, so at that time it can render/create DOM. No need to dispatch an Event to yet another render() methodMike is not completely right when he says to create shadowDOM in the
connectedCallback
. You can; but you don’t want to because theconnectedCallback
can run multiple times.The
constructor
is the perfect place to create shadowDOMSVG Namespaces are a pain. But the Browser will set the correct NameSpace when you add content as HTML (instead of DOM nodes)
That gives us:
Notes
Now I don’t care less if you spend the rest of your career fixing alignment issues;
but others shouldn’t suffer from your rudeness; so I added
inline-block
andvertical-align
for the same reason I added
pathLength="360"
because others shouldn’t have to deal with weirdstroke-dasharray
fraction values because you are rude.added handling of
attributes
because showing off cool Web Components is cool, despite a rude OP.I am not going to tell you I did https://pie-meister.github.io
So all you need to do is: