Creating the TurboReader browser extension

I'm a full-stack developer from South Africa 🇿🇦. I love writing about JavaScript, HTML and CSS.
Search for a command to run...

I'm a full-stack developer from South Africa 🇿🇦. I love writing about JavaScript, HTML and CSS.
No comments yet. Be the first to comment.
Now that we created the TurboReader browser extension, I wanted to see what's involved in actually getting it in the stores. I decided to only ship it to the Chrome store for now. In future articles, I might go into the other stores as well. Submitti...
Most of you know me for my consistency, a golden arrow in my blog series. I've written 1000 articles in 1008 days! Almost an article a day, and my honeymoon was the only holiday I ever took. I'm super proud of this achievement; it has been a fantasti...

It's not the first time I'll be talking about community. I think it's an essential aspect of any successful tool. This shows in my previous explorations of Astro, Medusa, and now Vendure as well. All these products thrive in a super open, welcoming, ...

The cool part about Vendure is how easy it is to set up and how abstract each layer is. Basically, we get the following elements: External database Server Worker Admin UI Frontend While this is amazing, it also brings a bit of complexity when it co...

The previous article looked at customizing Vendure on a data and process level. In this article, we'll look at customizing emails, as they are often a big part of a webshop system. We'll be looking at two different layers of customization for customi...

Even though Vendure is a pretty significant project out of the box, in some cases, we might want to go in and modify some elements to work to our specific use case. In this article, I'll take a high-level look at some elements we can customize within...

So far, we have had a good mix of different elements. This article will show you how I made the turboReader browser extension.
For those unfamiliar with turbo reading, it's a concept where you modify words to be partially bold to increase reading speed. This concept is called Bionic reading.
Read more about Bionic reading.
In this article, I won't be using the API of bionic but instead opt for my scripted version of the output.
The result will be this fantastic extension that can work on any webpage.

Let's first start by setting up the project. Our extension will power with the click of the options button. So we will only need our manifest and background worker.
We'll start by creating a new folder and navigating to it.
mkdir turboReader && cd turboReader
Once inside, open the project and start by adding the manifest.json file.
{
"manifest_version": 3,
"version": "1.0",
"name": "TurboReader",
"description": "Become a faster reader with TurboReader",
"action": {},
"icons": {
"48": "icons/icon-48.png"
},
"background": {
"service_worker": "background.js"
},
"permissions": [ "activeTab", "scripting"]
}
As you can see, we use manifest version 3.
Then we also define an empty action object, which is needed to attach handlers to the action icon.
And we set our background worker to a file called background.js.
Lastly, we'll need permissions to inject scripting on the active tab.
Let's go ahead and create the background.js file.
First, we'll need to add the action that fires on clicking the options button.
chrome.action.onClicked.addListener(async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.scripting.executeScript({
target: { tabId: tab.id },
function: turboMode,
});
});
Here, we fetch the active tab and inject a function called turboMode.
Now go ahead and create the turboMode function.
const turboMode = () => {
// Our function
};
To achieve this function, we first need to retrieve all paragraphs on the page.
const paragraphs = document.getElementsByTagName('p');
Then we want to ensure we are not yet in turbo reading mode. We'll do this by checking if a specific class is added to the body.
const isTurboMode = document.body.classList.contains('trms');
Then we can loop over each paragraph and do something with each one.
for (const paragraph of paragraphs) {
if (isTurboMode) {
// Turbo mode is one
} else {
// Turbo mode is off
}
}
If turbo mode is on, we should return the paragraphs to their original form. We'll do that by replacing the innerText with its non-html version.
paragraph.innerText = paragraph.innerText;
Note: side effect could cause existing bold/italic markup to be removed. 😅
If we are not in turbo mode, we should take the text for each paragraph and split it by word. Then we'll map each word and eventually return them with a space in between.
paragraph.innerHTML = paragraph.innerText
.split(' ')
.map((word) => {
// Each word
})
.join(' ');
Now comes the fun part. We must evaluate which part of the word we want to show in bold.
My thoughts here are that we can roughly make half the word bold. To achieve that, we first need to determine the length of the word.
const length = word.replace(/[^a-zA-Z0-9]+$/, '').length;
We can then get the length of the part we'll make bold. If the word is one letter, it should always become bold. Else we floor half of the length.
const boldLength = length === 1 ? 1 : Math.floor(length / 2);
And then we return the word, but with the first half being bold.
return `<strong>${word.substring(0, boldLength)}</strong>${word.substring(
boldLength
)}`;
The last part outside of the loop is to add a body class determining if we are in turbo mode or not.
isTurboMode
? document.body.classList.remove('trms')
: document.body.classList.add('trms');
And that's it. Our full function will now look like this:
const turboMode = () => {
const paragraphs = document.getElementsByTagName('p');
const isTurboMode = document.body.classList.contains('trms');
for (const paragraph of paragraphs) {
if (isTurboMode) {
paragraph.innerText = paragraph.innerText;
} else {
paragraph.innerHTML = paragraph.innerText
.split(' ')
.map((word) => {
const length = word.replace(/[^a-zA-Z0-9]+$/, '').length;
const boldLength = length === 1 ? 1 : Math.floor(length / 2);
return `<strong>${word.substring(
0,
boldLength
)}</strong>${word.substring(boldLength)}`;
})
.join(' ');
}
}
isTurboMode
? document.body.classList.remove('trms')
: document.body.classList.add('trms');
};
You can also find the completed code in this GitHub repo.
Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter