Writing a Minutes to Read Counter: using HTML and Javascript
In this article will write a Javascript script to count how many minutes required to read text. This text maybe in an article or anywhere. Remarks on how you would expand the article are found down the end. The text we are analyzing will assume it is in English.
Will be using HTML for where the text lies and Javascript. So I assume basic proficiency in HTML DOM and Javascript functions.
Coding:
So basically we need 2 elements for this. One element to hold the minutes per minute and the other is the actual content text. They can be any elements. I am using <div>. Will tag both elements with their unique ids.
<div id="wpm"></div>
<div id="text">
<!-- TEXT GOES HERE -->
</div>
So you put the text with the <div> with id text. It can be a <p> or anything. Within that text, any number of HTML elements could exist but we will not count the actual elements’ text. Ok and the wpm id will be where we put x mins to read.
Coming up with the Javascript inside <script> elements. Will examine the text and clean all spaces and non-alphabet using Regular Expressions. In short, regular expressions are a string query i.e. match and replace language. It is so useful. It is used heavilly in validating strings, looking for patterns and replacing plenty of weird strings that would take plenty (I mean it) of if/else statements to do.
For now will assume that the text is loaded on page load. For it to work, <script> must be placed at the end or at least after the text id. You can use <script defer> to put the script up above the document however, will need to handle DOM update event first then modify content.
The script:
<script>
(() => {
const text = document.getElementById("text").innerText;
const count = text.replace(/[^w ]/, "").split(/\s+/).length;
const mins = Math.floor(count / 250) + 1;
document.getElementById("wpm").innerText = `${mins} mins to read`;
})();
</script>
Will walk through the code. First running it as a function to make sure it loads after DOM done loading. This is crucial. Note the above (() => {})(); is basically similar to (function(){})(). This is called an anonymous function or Lambda function for the former, they are functionally the same.
Next is just plain old accessing the DOM to load the element text and accessing it’s innerText property. Note, you may point out there is innerHTML and textContent, all will load the content. That’s right except for the differences. innerText loads all text content except any HTML elements unlike textContent which does the same thing plus loads all actual spaces, that is innerText trims spaces longer than 1 to 1 space. innerHTML will return everything textContent does plus all HTML inside. If you are interested in details the following article does help:
https://www.w3schools.com/Jsref/prop_node_innertext.asp
Next will use the replace method to replace the text id element using the following regular expression [^w ]. Note // between the regular expression is a Javascript syntax to indicate this is a regular expression and not a string. Going back to the expression. We are starting a negated set [^], this means match anything not inside the expression following ^. Then the w , note the space after w, this means match all alphabet with space. If we took the ^ out we would be matching all alphabet, except in this case will be matching non-alphabet. Next will split the string outputted after the replace. Using \s+ means for each space represented as \s and then + any 1 or more. In the end will end up with an array of pure words. The following website does a pretty good job of simulating/learning regular expressions, I highly recommend it:
https://regexr.com/
Now diving the words count by 250 inside a floor function. And add 1, the 1 is arguably right. You can do without it. This mins variable is just a mathematical formula. The 250 is the average words read per minute by adult after quick searches online. You may have a different value, I am assuming English here. Will discuss more about this in the remarks section.
Later, will just fetch the wpm id element and assign it the value of mins.
Now done.
Remarks:
What you can do for a dynamically loaded text, in the case of Single Page Applications (SPA) where data is loaded dynamically or in a text-editor that counts as words change, you can listen to change events and recalculate each time.
So far I have assumed English, I don’t know how would it be for other non-Latin scripts. I assumed a word of 1 character to be of equal weight to a word of 20 characters. This is naive. You could assign points for each word for a more realistic calculation and then add them up. It gets complex really quickly once you add in length of words, commonality of words, is it read or comprehended by the reader and internationalization.
Now this calculation is done on the client side using the browser, computers have gotten faster. However, imagine the content is changing in a text editor, then other ways exist, scanning the entire text for each change is expensive. You can split the text into sections and so on. You would have to go beyond innerText.
You can always calculate it on the backend.
Conclusion:
This is simple after all, assuming English or just one language.
Thanks for reading this.