My second project for the Vanilla JS Academy was an application that counts the number of words and characters in a block of text. Here's how I approached the challenge.

  1. Character count
  2. Character and word count
  3. Announcing the count

Character count

Here's the HTML I started with:

<label for="text">Enter your text below.</label>
<textarea id="text"></textarea>

<p>You've written <strong><span id="character-count">0</span> characters</strong>.</p>

First things first, I created an immediately-invoked function expression (IIFE) to contain my code and keep it out of the global scope:

;(function() {

"use strict";

// Rest of code...

})();

Next, I declared my variables. I got a reference to the textarea and span elements:

// Get the #text element
var text = document.querySelector("#text");

// Get the #character-count element
var characterCount = document.querySelector("#character-count");

I then created two functions: one to get the character count, and another to update the displayed count:

/**
* Get the number of characters in a text area
* @param {Node} textArea The text area
* @returns {Number} The character count
*/

function getCount (textArea) {
return textArea.value.length;
}

/**
* Update the character count
*/

function updateCount () {
characterCount.textContent = getCount(this);
}

Finally, I initialized my script. I added an event listener to update the character count as the user types:

// Update the count when the value of #text changes
text.addEventListener("input", updateCount);

View demo on CodePen

Character and word count

First, I created another span element to hold the word count:

<p>You've written <strong><span id="word-count">0</span> words</strong> and <strong><span id="character-count">0</span> characters</strong>.</p>

I created a new variable called wordCount to reference the new span element:

// Get the #word-count element
var wordCount = document.querySelector("#word-count");

I then did a few things:

  1. Created a new getWordCount() function
  2. Renamed my getCount() function to getCharacterCount()
  3. Modified the updateCount() function to update both counts
/**
* Get the number of words in a text area
* @param {Node} textArea The text area
* @returns {Number} The word count
*/

function getWordCount (textArea) {

// Trim whitespace from the value
var value = textArea.value.trim();

// If it's an empty string, return zero
if (!value) return 0;

// Otherwise, return the word count
return value.split(/\s+/).length;

}

/**
* Get the number of characters in a text area
* @param {Node} textArea The text area
* @returns {Number} The character count
*/

function getCharacterCount (textArea) {
return textArea.value.length;
}

/**
* Update the word and character counts
*/

function updateCounts () {

// Update the word count
wordCount.textContent = getWordCount(this);

// Update the character count
characterCount.textContent = getCharacterCount(this);

}

Finally, I initialized my script. This is the same as before, except I'm referencing the renamed function, updateCounts() (plural, not singular):

// Update both counts when the value of #text changes
text.addEventListener("input", updateCounts);

View demo on CodePen

Announcing the count

This final part is all about accessibility. The challenge was to make sure the updates to the wordCount and characterCount elements are actually announced to screen readers.

The first thing I did was update my HTML. I removed the span elements and put all the text inside a single p element with an ID of count. The important thing to note is that I added the aria-live attribute and set it to polite.

<label for="text">Enter your text below.</label>

<textarea id="text"></textarea>

<p id="count" aria-live="polite">You've written 0 words and 0 characters.</p>

As my good friend Chris Ferdinandi says in his article How and why to use aria-live:

The aria-live attribute lets screen readers know that content in a particular element is going to change dynamically, and that they should pay attention to it and announce those changes.

Its value can be set to off (the same as not using it at all), assertive (in which screen readers interrupt user actions to announce changes), and polite (which tells the screen reader to wait until the user is done to announce updates).

Generally speaking, we should always use polite.

I replaced my wordCount and characterCount variables with a single count variable that references the p element:

// Get the #count element
var count = document.querySelector("#count");

Then I modified my updateCounts() function:

/**
* Update the word and character counts
*/

function updateCounts () {

count.textContent = (
"You've written " + getWordCount(this) + " words " +
"and " + getCharacterCount(this) + " characters."
);

}

And I initialized my script in the same way as before.

View demo on CodePen