Summary: in this tutorial, you’ll learn how to build a Word Counter application using vanilla JavaScript.
Here is the Word Counter App that you’re going to build.
Create the project structure
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.
The final project folder structure will look like this:
Create the HTML file
First, edit the index.html
file and place the CSS and JavaScript files in the index.html
file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript Word Counter</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<script src="js/word-counter.js"></script>
<script src="js/app.js"></script>
</body>
</html>
Code language: HTML, XML (xml)
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 :
- The
<textarea>
element will allow you to enter text. - And the
<div>
element will show the number of characters and words entered into the<textarea>
element.
By default the <div>
element should show 0 characters and 0 words.
Second, add the <textarea>
and <div>
elements to the index.html
file after the opening <body>
tag and before the first <script>
tag:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript Word Counter</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<h1>Word Counter</h1>
<label for="text">Enter some text below:</label>
<textarea id="text" rows="10" cols="60"></textarea>
<div id="stat">You've written 0 words and 0 characters.</div>
<script src="js/word-counter.js"></script>
<script src="js/app.js"></script>
</body>
</html>
Code language: HTML, XML (xml)
Create the WordCounter class
First, you’ll create the WordCounter
class in the word-counter.js
file:
class WordCounter {
}
Code language: JavaScript (javascript)
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.
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:
class WordCounter {
constructor(inputText) {
this.inputText = inputText;
this.inputText.addEventListener('input', this.count);
}
count(){
}
}
Code language: JavaScript (javascript)
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.
Third, add a new method to the WordCounter
class, which calculates the number of characters and words:
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,
};
}
}
Code language: JavaScript (javascript)
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:
...
count() {
let wordStat = this.getWordStat(this.inputText.value.trim());
// how to expose the wordStat to the outside
// ..
}
Code language: JavaScript (javascript)
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:
emitEvent(wordStat) {
// Create count event
let countEvent = new CustomEvent('count', {
bubbles: true,
cancelable: true,
detail: {
wordStat
}
});
// dispatch the count event
this.inputText.dispatchEvent(countEvent);
}
Code language: JavaScript (javascript)
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:
count(){
let wordStat = this.getWordStat(this.inputText.value.trim());
this.emitEvent(wordStat);
}
Code language: JavaScript (javascript)
The WordCounter
class will look like the following:
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,
};
}
}
Code language: JavaScript (javascript)
Add logic to app.js file
First, select the <textarea>
and <div>
element using the querySelector()
method:
const inputText = document.querySelector('#text');
const statElem = document.querySelector('#stat');
Code language: JavaScript (javascript)
Second, create a new instance of the WordCounter
class and pass the inputText
element into its constructor:
new WordCounter(inputText);
Code language: JavaScript (javascript)
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:
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>`;
}
Code language: HTML, XML (xml)
Fourth, add an event listener to count
even and execute the render()
method each time the count
event occurs:
inputText.addEventListener('count', render);
Code language: JavaScript (javascript)
The app.js
will look like the following:
const inputText = document.querySelector('#text');
const statElem = document.querySelector('#stat');
// 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);
Code language: JavaScript (javascript)
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
Code language: HTML, XML (xml)
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.
To prove this, you can log the this
value inside the count()
method as follows:
count() {
console.log(this);
}
Code language: JavaScript (javascript)
… and refresh the index.html
again, you’ll see the <textarea>
element in the console every time you type some text in the <textarea>
:
<textarea id="text" rows="10" cols="60"></textarea>
Code language: HTML, XML (xml)
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:
constructor(inputText) {
this.inputText = inputText;
this.inputText.addEventListener('input', () => {
this.count();
});
}
Code language: JavaScript (javascript)
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:
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,
};
}
}
Code language: JavaScript (javascript)
Click here to see the Word Counter App in action.
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.