DEV Community

A0mineTV
A0mineTV

Posted on

๐Ÿš€ Building a Dynamic Word Counter Web Component with MutationObserver

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:

  1. Displays the number of words in a contenteditable element.
  2. Updates dynamically as users type, delete, or modify text.
  3. 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>
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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 or textContent.
  • 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)