Skip to content

Instantly share code, notes, and snippets.

@bobmonsour
Last active April 1, 2023 22:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bobmonsour/53ea41c50bec94be394a9314858dad1d to your computer and use it in GitHub Desktop.
Save bobmonsour/53ea41c50bec94be394a9314858dad1d to your computer and use it in GitHub Desktop.
This code can be used as an eleventy filter to create the reading time (in minutes) of a piece of content.
/*
* Calculate the reading time, in minutes, of a post
*
* Assumptions:
* - average reading time is 240 words per minute
* source: https://bit.ly/3HCogSr, "Most Comprehensive
* Review To Date Finds The Average Person’s Reading
* Speed Is Slower Than Previously Thought"
*
* Output:
* - reading time is rounded to the nearest minute
* - in the case of less than 1 minute, reading time is
* displayed as "less than a minute"
*
* @param {String} text
*/
module.exports = function (text) {
var content = new String(text);
const speed = 240; // reading speed in words per minute
// remove all html elements
var re = /(&lt;.*?&gt;)|(<[^>]+>)/gi;
var plain = content.replace(re, "");
// replace all newlines and 's with spaces
var plain = plain.replace(/\s+|'s/g, " ");
// create array of all the words in the post & count them
var words = plain.split(" ");
var count = words.length;
// calculate the reading time
var readingTime = Math.round(count / speed);
if (readingTime === 0) {
return "Less than 1 minute to read";
} else if (readingTime === 1) {
return "1 minute to read";
} else {
return readingTime + " minutes to read";
}
};
@Aankhen
Copy link

Aankhen commented Mar 31, 2023

A couple of unsolicited suggestions as I just came across this:

  1. Replace var re = /(&lt;.*?&gt;)|(<.*?>)/gi; with var re = /(&lt;.*?&gt;)|(<[^>]+>)/gi;. .*? and other non-greedy matching operators can be faster, but not in this case. Is checking for escaped HTML appropriate, BTW?
  2. Normalize all whitespace: var plain = plain.replace(/\s+|'s/g, " ");. You might also want to think about Unicode apostrophes and things like 't, 're, and so on. Maybe plain.replace(/\s+|(?:(\p{Letter})\p{Punctuation}(\p{Letter}))/gu, "$1 $2");, although that’s just me trying to write it on the fly here.

@bobmonsour
Copy link
Author

Thanks for your thoughts. I had to think for a second about how I originally came up with that gist. I found a blog post by Phil Hawksworth of Netlify where he was showing how he created a fast search feature for his blog. He has a "sqash" filter that he created and the code above is based on that. I recall spending a lot of time wanting to understand how the regex worked. They are not my strong suit. I'll take a close look at your suggestion and see if I can understand it. I really appreciate you taking the time. Here's a link to Phil's squash filter.

https://github.com/philhawksworth/hawksworx.com/blob/8c96ba2541c8fd6fe6f521cdb5e17848c231636c/src/site/_filters/squash.js

@Aankhen
Copy link

Aankhen commented Apr 1, 2023

I understand. Regexes can be quite opaque. I recommend using a tool like regex101. It’ll explain every character of the regex and let you try it on different strings in various modes. And, of course, please feel free to ask me—whether here or on Discord—if you have any questions: my first programming language was Perl, so I have an unhealthy degree of comfort with regexes.

He has a "sqash" filter that he created and the code above is based on that.

Thanks for the link. This works as a quick-and-dirty solution for their use case, but I think the changes I suggested will make it more generally useful and accurate. If you want to ignore small words like in the original code, I would suggest defining a Set of those words and filtering the array before counting (var count = words.filter((w) => !WORDS_TO_IGNORE.has(w)).length;) instead of trying to ignore them via regex. In that case, you’d also need var content = text.toLowerCase();.

@bobmonsour
Copy link
Author

bobmonsour commented Apr 1, 2023

Cool. Thanks for the link to regex101. Looks very handy. I'm going to make an update to my Calculating blog using your suggestions (at some point). Right now, I'm scouring the net for the next issue of The 11ty Bundler. I am thinking that if I continue to generate the Bundler, it will probably turn into its own website. I broke down and bought the 11tybundler.com domain yesterday. Not that that necessarily signals commitment as I have a handful of other unused domains that started as ideas in my head. At least with this one, I've already started something that could make more sense for me to head down that path. What I like about the idea is that it will give me more reasons to keep learning about Eleventy, JS, and CSS.

@Aankhen
Copy link

Aankhen commented Apr 1, 2023

Sounds like a great idea to me. I look forward to reading more, if you do put together more issues (so to speak). 😊

@bobmonsour
Copy link
Author

This gist has been updated with the help of @Aankhen. Many thanks. The resulting word counts are comparable to the earlier regex I used.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment