How to make a word count tool with vanilla JavaScript
Tags: javascript, programming, oop, beginners
Published: 2022-05-04 11:21:56

In this tutorial, you’ll learn how to build a Word Counter application using vanilla JavaScript. 
Below is what we will be doing.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kiotooojjgvabpyp8v8u.png)


You will be able to achieve what you see in the above diagram as soon as you’re done, with this content.

##Introduction
As a beginner or intermediate developer, you will definitely get to a point where you will need to build something that counts words and character, and can be used to build a chat app or comment section. And there is a high demand from intermediate developers about building this word counter tool.

###Prerequisite
The only item you will need, to build the word counter app with me. Although you don’t need much
 Sublime text or Vs code
 Browser of your choice

###Coding the Word Counter App
First, create the project folder called word-counter.
Second, under the word-counter project, create the css and js folders, which will store CSS and JavaScript files accordingly.
Third, create a style.css file inside the css folder, and two JavaScript files called word-counter.js and app.js inside the js folder. Finally, create the index.html file in the project root folder.


###Create the HTML file
After creating the HTML file, The WordCounter app will have a simple <textarea> element.
When you enter some text, it’ll show the number of characters and words that you’ve entered.
To do so, you’ll need to have <textarea> and <div> elements:

```HTML
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>word count</title>
	<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
	<h1>Word Count</h1>

	<label>Enter Some Text Below:</label>
	<textarea cols="60" rows="10" id="text"></textarea>
	<div id="stat">You have written 0 words and 0 character</div>

	<script src="wordCounter.js"></script>
	<script src="app.js"></script>

</body>
</html>
```
Create the wordCounter class
First, you’ll create the WordCounter class in the word-counter.js file:
```JS
class WordCounter { 

}
```


The WordCounter class will accept a <textarea> element. It’ll listen to the input event of the <textarea> element and calculate the number of characters and words that the <textarea> element has.
Second, add the constructor to the WordCounter class. The constructor will accept a <textarea> element.
```js
class WordCounter { 
   constructor(inputText) { 
   this.inputText = inputText;
   this.inputText.addEventListener('input', this.count);
   } 
  count(){ 
  } 
}
```
Inside the constructor, you’ll initialize the inputText property of the class to the
 inputText argument and attach the input event listener to the inputText element:

The this.count() method will execute every time the input event occurs. We’ll go back to implement the logic for the count() method later.
```js
class WordCounter { 
  constructor(inputText) { 
  this.inputText = inputText;
this.inputText.addEventListener('input', this.count); 
  } 
  count(){ 
} 
getWordStat(str) { 
   let matches = str.match(/\S+/g); 
     return { 
   characters: str.length, words: matches ? matches.length : 0,
 };
 }
}
```
Third, add a new method to the WordCounter class, which calculates the number of characters and words:
The getWordStat() method uses a regular expression /\S/g to return the number words of a string. It also uses the string length property of the input string str to get the number of characters.

Third, the count() method will need to call the getWordStat() to calculate the number of words and characters of the inputText element.
To get the text of the <textarea> element, you use its value property:

And the count() method also needs to communicate with the outside the number of words and characters.
To do this, you have two options: using a callback and a custom event. We’ll use a custom event in this tutorial.
If you want to know how to use a callback, check it out the countdown timer tutorial.
Fourth, add a new method called emitEvent to the WordCounter class:

```js
emitEvent(wordStat) { 
// Create count event 
  let countEvent = new CustomEvent('count', { 
  bubbles: true,
  cancelable: true, 
  detail: { 
  wordStat 
  } 
  });
this.inputText.dispatchEvent(countEvent); 
}
```

The emitEvent() method accepts a wordStat object. Inside the method, we create a custom event for the inputText element called count using the CustomEvent constructor and dispatch the count event using the dispatchEvent method.
Later, you’ll attach an event listener to the count event and access the wordStat object using the event.detail.wordStat syntax.
The emitEvent() should be called every time the input event occurs. Therefore, we invoke the emitEvent() inside the count() method:

```js
Class wordCounter{
    constructor(inputText) { 
    this.inputText = inputText; 
    this.inputText.addEventListener('input', this.count); 
    } 
  count(){
     let wordStat = this.getWordStat(this.inputText.value.trim()); 
  this.emitEvent(wordStat); 
 }
emitEvent(wordStat) { 
// Create count event 
 let countEvent = new CustomEvent('count', { 
 bubbles: true, 
 cancelable: true,
 detail: { 
 wordStat 
 } 
}); 
// dispatch the count event 
 this.inputText.dispatchEvent(countEvent); 
}
```
// The function Below will calculate the number of characters

```js
getWordStat(str) { 
  let matches = str.match(/\S+/g); 
  return { 
  characters: str.length, 
  words: matches ? matches.length : 0, 
  }; 
 }
}
```
The wordCounter.js will look like the following.

Add logic to app.js file
```js
const inputText = document.querySelector('#text'); 
const statElem = document.querySelector('#stat');
```
First, select the <textarea> and <div> element using the querySelector() method:

Second, Create a new instance of the wordcounter class And pass in the inputText element into it’s constructor:
```js
new WordCounter(inputText);
```

Third, define a new function called render() that updates the word and character counts to the statElem element.
The render() function accepts a custom event object:

```js
const render = (event) => { 
  statElem.innerHTML = `<p>You've written <span class="highlight">
${event.detail.wordStat.words} words</span>and <span class="highlight">
${event.detail.wordStat.characters}characters</span>.</p>`; 
}
```

Fourth, add an event listener to count even and execute the render() method each time the count event occurs:

```js
inputText.addEventListener('count', render);

const inputText = document.querySelector('#text'); 
const statElem = document.querySelector('#stat');
```

```js
 // create a new instance of WordCounter 
new WordCounter(inputText);

const render = (event) => { statElem.innerHTML = `<p>You've written
<span class="highlight">${event.detail.wordStat.words} words</span>and
<span class="highlight">${event.detail.wordStat.characters}
 characters</span>.</p>`; 
} 
inputText.addEventListener('count', render);
```

The app.js will look like the following:

Now, if you open the index.html file in the web browser, you’ll see the following error:
Uncaught TypeError: Cannot read property 'value' of undefined at HTMLTextAreaElement.count

And the problem occurred in the count() method of the WordCounter class:


It’s showing that the this.inputText is undefined. Therefore, accessing the value property of the this.inputText causes an error.
Solve this issue
When an input event occurs on the inputText element, the count() method executes.
And the object that executes the count() method is the inputText object, not the instance of the WordCounter class.
It means that inside the count() method, the this value references the inputText element, not the WordCounter object.

```js
count() { 
   console.log(this); 
}
```
To prove this, you can log the this value inside the count() method as follows:

```HTML
<textarea id="text" rows="10" cols="60"></textarea>
```
… and refresh the index.html again, you’ll see the <textarea> element in the console every time you type some text in the <textarea>:

Since the this value inside the count() method references the <textarea> element, it doesn’t have the inputText property. And it also doesn’t have the emitEvent() method.
To fix the issue, you need to change the event listener to an arrow function like this:

```js
constructor(inputText) { 
 this.inputText = inputText; 
 this.inputText.addEventListener('input', () => { 
 this.count(); 
 }); 
}
```

When you use the arrow function, the this value references the object of the surrounding block which is the WordCounter in this case. In other words, you can access all the properties and methods of the WordCounter in the count() method.

The final WordCounter class will look like this:

```js
class WordCounter { 
  constructor(inputText) { 
  this.inputText = inputText; 
  this.inputText.addEventListener('input', () => { 
  this.count(); 
  });
} 
count() { 
let wordStat = this.getWordStat(this.inputText.value.trim());
This.emitEvent(wordStat);
}
 emitEvent(wordStat) { // Create count event 
 let countEvent = new CustomEvent('count', { 
 bubbles: true, 
 cancelable: true, 
 detail: { 
 wordStat 
 } 
 }); // dispatch the count event 
 this.inputText.dispatchEvent(countEvent); 

 } 
 getWordStat(str) { 
   let matches = str.match(/\S+/g); 
   return { 
   characters: str.length, 
   words: matches ? matches.length : 0, 
  }; 
 } 
}
```
##Summary
In this tutorial, you have learned how to develop a Word Counter app using vanilla JavaScript. And the following are the key takeways:

- How to create and emit a custom event
- How to resolve the this issue using arrow functions.
       #About the Author
**Emmanuel C. Okolie** kick-started his journey as a software engineer in 2020. Over the years, he has grown full-blown skills in JavaScript, Php, Html & Css and more.
He is currently freelancing, building websites for clients, and writing technical tutorials teaching others how to do what he does.

**Emmanuel C. Okolie** is open and available to hear from you. You can reach him on [Linked-In](https://www.linkedin.com/in/emmanuel-okolie-435588238/), [Facebook](https://facebook.com/emmanuel.okolie.56481), [Github](https://github.com/emmykolic), [Twitter](https://twitter.com/emmykolic), or on his [website](https://emmykolic.github.io).