const styleRules = ` .rating {font-size: 50px; color: orange; cursor: pointer }` class MyRating extends HTMLElement { constructor() { super() this._maxValue = 5 this._value = 4 this.attachShadow({mode: "open"}) } connectedCallback() { this.createComponent() } get maxValue() { return this._maxValue } set maxValue(val) { this._maxValue = val this.setAttribute("max-value", val) } get value() { return this._value } set value(val) { this._value = val this.setAttribute("value", val) } static get observedAttributes() { return ["max-value", "value"] } attributeChangedCallback(name, oldValue, newValue) { if (oldValue !== newValue) { switch (name) { case "max-value": this._maxValue = newValue break case "value": this._value = newValue break default: } this.replaceStarList() } } replaceStarList() { let starNode = this.shadowRoot.children[1]; if (starNode) { let starList = this.createStarList(); starNode.remove(); this.shadowRoot.appendChild(starList); } } createComponent() { this.createStyle() let starList = this.createStarList() this.shadowRoot.appendChild(starList) } createStyle() { let style = document.createElement("style") style.appendChild(document.createTextNode(styleRules)) this.shadowRoot.appendChild(style) } createStarList() { let div = document.createElement("div") for (let i = 1; i <= this.maxValue; i++) { let star = i <= this.value ? this.createStar("★", i) : this.createStar("☆", i) div.appendChild(star) } return div } createStar(starCode, i) { let span = document.createElement("span") span.setAttribute("class", "rating") span.innerHTML = starCode span.addEventListener("click", () => { this.value = i }, false) return span } } customElements.define("my-rating", MyRating)