ShadowDom experiments
by Nicholas Berlette
HTML
<div contenteditable="true">
Custom Web Components! Try editing this text and watch the counter keep up!
<div is="word-count"></div>
</div>
JavaScript
class WordCount extends HTMLDivElement {
static get observedAttributes() {
return ["textContent", "innerText"];
}
static count(text){
return text.trim().split(/\s+/g).filter(a => a.trim().length > 0).length;
}
constructor() {
// Always call super first in constructor
super();
this._shadow = this.attachShadow({mode: 'open'});
}
connectedCallback() {
const text = document.createTextNode(this.textContent);
this._shadow.appendChild(text);
this.update();
setInterval(() => this.update(), 500);
}
update() {
if (!this._count) {
this._count = document.createElement("span");
this._count.id = "__word_count";
this._count.style.fontSize = "0.8em";
this._count.style.border = "1px dashed #999";
this._count.style.display = "block";
this._count.style.marginTop = "5em";
this._count.style.padding = "0.5em 1em";
this._count.style.borderRadius = "1em";
this._count.contenteditable = true;
this._count.innerText = this.render();
this._shadow.appendChild(this._count);
} else {
this._count.innerText = this.render();
}
}
render() {
return `Words: ${WordCount.count(this.parentNode.textContent)}`;
}
attributeChangedCallback(name, oldValue, newValue) {
console.log("Attribute Changed: (%s) - %s -> %s", name, oldValue, newValue);
// count words in element's parent element
if (name === "textContent") {
this._count.innerText = `Words: ${WordCount.count(newValue)}
Old Value: ${oldValue}
New Value: ${newValue}
`;
}
}
}
// Define the new element
customElements.define('word-count', WordCount, { extends: 'div' });