Introduction
When faced with inputs that have a limited number of characters allowed, I found it was really difficult to estimate what 200 or 500 meant, let alone trying to track it when using a screen reader.
The following script takes the guesswork out of it. It dynamically counts and displays the number of characters remaining based on the input's maxlength
value. The script also provides a secondary counter for screen readers.
Demo
As you type in the following field, notice how the first counter updates dynamically as you type whereas the second only updates after two seconds of inactivity. The second counter is normally hidden visually but is left on for this demo.
See the Pen Dynamic and accessible input character counter using JavaScript by Eric Niquette (@ericniquette) on CodePen.
How it works
The script binds itself to any input with the .counted
class and looks for the maxlength
attribute. It uses that value to populate the next .screen-only
and .sr-only
elements. This is repeated for every individual input.
As the user types in the field, the number of characters remaining is counted based on the maxlegnth
and this value is dynamically appended to the .screen-only
element. After two second of inactivity, the .sr-only
element is updated.
The inactivity timer takes all events into consideration and does not only track keyboard input. Pasting text, for example, will reset the timer.
Solution
HTML
The script relies on elements with specific class names and attributes: an input or textarea with the .counted
class and a maxlength
attribute. It also requires two elements, like a paragraph, with the .screen-only
and .sr-only
classes that the script can replace.
The rest of the HTML is a label for our input and a few supporting ARIA attributes for screen readers.
If you want to have multiple instances or inputs, update the "1" labels and ID values.
<label for="input_name1">Please enter text (Required)</label>
<textarea id="input_name1" maxlength="1000" class="counted" aria-describedby="counter_description1"></textarea>
<p class="counter screen-only" aria-hidden="true">Enter up to 1000 characters</p>
<p class="counter sr-only" id="counter_description1">Enter up to 1000 characters</p>
CSS
The only CSS needed is a class to visually hide the second counter. This is my preferred method but feel free to use whichever technique you're most comfortable with.
.sr-only {
position: absolute;
left: -9999em;
opacity: 0;
overflow: hidden;
top: -9999em;
}
JavaScript
The script is relatively straightforward. The delay is, by default, set to 2000 milliseconds can be tweaked to suit your needs.
document.addEventListener("DOMContentLoaded", function () {
var timer = '';
var counted = document.querySelectorAll('.counted');
counted.forEach(function (elem) {
elem.addEventListener("input", function () {
clearTimeout(timer);
var pause = this;
var limit = this.getAttribute("maxlength");
var remainingChars = limit - this.value.length;
if (remainingChars <= 0) {
this.value = this.value.substring(0, limit);
}
var screenOnlyElem = this.nextElementSibling;
while (screenOnlyElem && screenOnlyElem.classList.contains('screen-only') === false) {
screenOnlyElem = screenOnlyElem.nextElementSibling;
}
if (screenOnlyElem) {
screenOnlyElem.textContent = remainingChars <= -1 ? 0 : remainingChars + ' character(s) remaining';
}
timer = setTimeout(function () {
var srOnlyElem = pause.nextElementSibling;
while (srOnlyElem && srOnlyElem.classList.contains('sr-only') === false) {
srOnlyElem = srOnlyElem.nextElementSibling;
}
if (srOnlyElem) {
srOnlyElem.textContent = remainingChars <= -1 ? 0 : remainingChars + ' character(s) remaining';
}
}, 2000);
});
elem.dispatchEvent(new Event('input'));
});
});