Customized Word Count Component

Count the words of an input box of textarea, identified by word-count attribute. Shows how you may extend an element to perform tasks on the DOM. Extend the span with word-count to check the parent nodes children for the input/textarea with attribute word-count, then counting all words in it. Bootstraped from a bootstrap file, by importing into a bigger application or direct as html script (ensure you build fallback for IE support).

<span is="word-count"></span>

Word Count Usage <span is="word-count">

<!-- Polyfill -->
<script src="/node_modules/promise-polyfill/dist/polyfill.min.js"></script>
<script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>

<!-- Bootstrap component only, ensure you have built fallback for IE support too (.js)! -->
<script type="module" src="./your/path/word-count.mjs"></script>
<script nomodule src="./your/path/word-count.js"></script>

<!-- Or add your switch import to a bootstrap js file/app using ES6 import and use that instead of the component directly... -->
<script type="module" src="./your/path/index.mjs"></script>
<script nomodule src="./your/path/index.js"></script>

<!-- example basic fouc resolution -->
	[fouc] { opacity: 0; transition: opacity 200ms ease-in-out; }
	[fouc="loaded"] { opacity: 1; }


<div class="info">
	<div class="class">
		<div class="row" fouc>
			<div class="col-sm-12">
				<textarea word-count style="width: 100%;">Try writing some text in here!</textarea>
				<span is="word-count" class="label label-success"></span>


<!-- fouc for individual components -->
<script> setTimeout(() => document.querySelector('[fouc]').setAttribute('fouc', 'loaded'), 1000); </script>

WordCount Class word-count.mjs

import { CustomHTMLSpanElement } from '../../../node_modules/custom-web-component/index.js';

 * @public @name WordCount
 * @extends CustomHTMLSpanElement
 * @description Library web component extending the span element with a word count from an input/textarea
class WordCount extends CustomHTMLSpanElement {

     * @public @constructor @name constructor
	 * @description Triggered when component is instantiated (but not ready or in DOM, must call super() first)
    constructor() {
        // Always call super first in constructor

        // count words in element's parent element
        this.countable = this.parentNode.querySelector('input[word-count]') || this.parentNode.querySelector('textarea[word-count]');

     * @public connected()
     * Invoked when node is connected/added to the DOM
    connected() {
        if (!this.countable) return;
        this.countable.addEventListener('input', this._countableEvent = this._countWords.bind(this));
	 * @public disconnected()
	 * Invoked when node is disconnected/removed from the DOM
    disconnected() {
        if (!this.countable) return;
        this.countable.addRemoveListener('input', this._countableEvent = this._countWords.bind(this));

	 * @public _countWords()
	 * The button got clicked
	 * @param {Event} ev The event that kicked the method
    _countWords(ev) {
        let count = this.countable.value.split(/\s+/g).filter((e) => e != '').length;
        this.innerText = `Words: ${count}`;

// Define the new element
customElements.define('word-count', WordCount, { extends: 'span' });