Building a Dynamic Word Counter Web Component with MutationObserver
Web Components are a great way to create reusable, encapsulated, and custom HTML elements. In this tutorial, we'll build a simple word counter component <word-count>
that dynamically updates as text is added or removed in an editable area. We'll leverage the power of JavaScript's MutationObserver
to detect content changes efficiently.
The Goal
We want to create a <word-count>
component that:
- Displays the number of words in a
contenteditable
element. - Updates dynamically as users type, delete, or modify text.
- Is encapsulated and reusable.
The HTML Structure
Here's the basic structure we'll use:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic Word Count Component</title>
</head>
<body>
<h1>Dynamic Word Count Example</h1>
<article id="editable" contenteditable="true">
<h2>Start typing here!</h2>
<p>This is an editable paragraph. Add your text...</p>
</article>
<word-count></word-count>
<script src="main.js"></script>
</body>
</html>
The <article>
element is editable, and the <word-count>
component will display the word count dynamically.
The JavaScript Code
Here's the full implementation of the WordCount
custom element using MutationObserver
:
class WordCount extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({ mode: 'open' });
this.text = document.createElement('span');
this.text.textContent = "Words: 0";
this.shadow.appendChild(this.text);
}
connectedCallback() {
// Find the nearest editable parent
const parent = this.closest('[contenteditable]') || this.parentElement;
if (!parent) {
console.error('WordCount component must be inside or near an editable parent.');
return;
}
// Observe changes in the parent
this.observer = new MutationObserver(() => this.updateCount());
this.observer.observe(parent, { childList: true, subtree: true, characterData: true });
// Initial word count
this.updateCount();
}
disconnectedCallback() {
// Stop observing when the component is removed
if (this.observer) {
this.observer.disconnect();
}
}
countWords(node) {
const text = node.innerText || node.textContent || '';
return text.trim().split(/\s+/).filter(word => word.length > 0).length;
}
updateCount() {
const parent = this.closest('[contenteditable]') || this.parentElement;
if (parent) {
const wordCount = this.countWords(parent);
this.text.textContent = `Words: ${wordCount}`;
}
}
}
customElements.define('word-count', WordCount);
How It Works
The dynamic word counter leverages the power of Web Components and the MutationObserver API to track changes in the DOM and update the word count in real time. Hereโs a breakdown of how it works:
1. connectedCallback
The connectedCallback
method is called when the custom element is added to the DOM. In this step:
- The component locates its nearest editable parent element using
closest('[contenteditable]')
or defaults to its parent element. - If no suitable parent is found, it logs an error to the console and stops execution.
2. MutationObserver
A MutationObserver
is created to monitor changes within the editable parent. It listens for:
- Child list changes: Adding or removing text nodes or HTML elements.
- Character data changes: Modifications to text within existing nodes.
- Subtree changes: Nested changes within child nodes.
When changes are detected, the observer triggers the updateCount()
method to recalculate the word count.
3. Dynamic Word Counting
The countWords()
method processes the parent node's content:
- It extracts text using
innerText
ortextContent
. - The text is split into an array of words using a regular expression (
\s+
). - Empty or invalid entries are filtered out to ensure accurate word counts.
4. disconnectedCallback
If the <word-count>
component is removed from the DOM, the MutationObserver
is disconnected. This cleanup step prevents unnecessary monitoring and avoids memory leaks.
5. Real-Time Updates
The word count is displayed in the componentโs shadow DOM and updated dynamically as changes occur in the editable parent. This ensures a seamless user experience without the need for manual refreshes or button clicks.
Why Use MutationObserver ?
Detects a wide range of changes (e.g., text, nodes, attributes).
Works asynchronously to avoid blocking the main thread.
Is highly performant for dynamic applications.
ย Live Demo
To see it in action, paste the HTML and JavaScript into your favorite code editor and open the HTML file in a browser. Start typing in the editable area, and you'll see the word count update dynamically.
Wrapping Up
With just a few lines of code, we built a reusable web component that dynamically tracks word count. This approach can be extended for more complex scenarios like real-time text analysis, input validation, or collaboration tools.
If you found this tutorial helpful, feel free to leave a comment or share how you used this component in your projects!
Top comments (0)