Build A Text Web-Highlighter as a Chrome Extension

Book with text marker in it
Photo by Aaron Burden / Unsplash

Learn to build a Chrome extension with Web Components.

I always loved the web highlighter, which appears when selecting text in an article on Medium.com. I thought that it would be nice to have such functionality on each website on the internet.

Medium.com web highlighter
Medium.com web highlighter

I began to look for a chrome extension that provides a way to save text and bookmarks in a better-organized way. Several extensions function as a text marker, but none of them really acted according to my conceptions. That was when I decided to develop my own Web Highlights Chrome Extension.

What we build

In this article, I want to show you a step-by-step guide on setting up a chrome extension that shows a text highlighter, like the one here on Medium, in the form of a Web Component. We are going to keep things simple by using basic Javascript and CSS.

Here is what we are gonna build:

Video of highlighter in action
Our highlighter on: https://developer.mozilla.org/en-US/docs/Web/Web_Components

For those of you who just want to go straight to the source code. You will find it here.

Let’s get started

First of all, we need to create a manifest.json file. This is the entry point of our extension, which defines metadata, such as name and version, as well as additional functionalities.

Let’s create a manifest.json and add some metadata:

The first three values name , version and manifest_version are enough to create our first chrome extension.

Install the extension

Open your Chrome browser and navigate to chrome://extensions.

Enable Developer Mode by clicking the toggle switch next to Developer mode. Click the Load unpacked button and select the directory with ourmanifest.json.

Chrome Store - How to load unpacked

Congratulations! You’ve just created your first Chrome Extension!

Add some functionality

We have just installed our own extension. Still, currently, it doesn't do anything because we haven’t added functionality to our manifest.json .

To make our text-highlighter work we need a functionality that can read the DOM and make changes to it. For that, we will create content scripts. The chrome developer documentation defines content scripts as follows:

Content scripts live in an isolated world, allowing a content script to make changes to its JavaScript environment without conflicting with the page or other extensions’ content Scripts.

Let’s create two files, each with one alert, to ensure that they are successfully loaded by our extension. Those files will act as our content scripts.

The content.js script is the entry point of our extension. It will be responsible for creating the Web Component, that will be defined in the medium-highlighter.js file.

Let’s adjust our manifest.json to inject our scripts:

To use our statically declared scripts we need to register them under the content_scripts field. This field needs to define the required attribute matches to specify on which pages the content script will be injected into. For that, you can specify several match patterns. We will just use the special pattern <all_urls> to match any URL that starts with a permitted scheme- for example http or https .

We also define the optional field js. Here we define our scripts to be injected into matching pages.

Let´s update our extension by pressing the refresh button of the extension on chrome://extensions . We can now navigate to any page and, if done correctly, two alerts should appear when the page is loaded.

To be honest a really annoying extension. So let’s get started with creating our text-highlighter Web Component.

Defining the Web Component

Finally, we set up the basic structure of our extension. We can now go ahead and create our text-highlighter in a familiar Javascript environment. Or better to say in the isolated world of content scripts.

Template

We want to show a button with an icon whenever a user selects text on websites. Additionally, we need an element that will be wrapped around the selected text to indicate a highlight on the page. That’s our template which will be rendered by our Web Component:

First of all, we create a constant for our highlight color to follow the DRY principle because we will use it again later. It is also noticeable that we will make use of the template HTML tag. This will be our wrapper element around highlighted text.

Template elements are one of the three main technologies of Web Components. They are used to hold HTML content that is not rendered immediately and can be instantiated subsequently during runtime. Since we will need text highlights many times and it should not be visible from the beginning, it makes sense to use this technology.

Styling

To make our highlighter look good and also appear at the right place we need to define some dynamic styling:

Per default, our highlighter should not be visible and its position should be in the upper left corner. We also re-use our highlightColor constant to color our marker icon.

Defining the Custom Element

Now it is time to define our Web Component and use our template with its styling. That’s how our custom element looks like:

Well, that’s a lot. Don’t worry I will guide you through it:

First, we will have a look at the constructor. Since we are inheriting from HTMLElementthe super() call is necessary to establish the correct prototype chain. Additionally, we define our custom-element medium-highlighter at the end of the file.

After the super call, we trigger our render function. You can see that we are attaching a shadow dom to our element. The shadow DOM is another main technology of Web Components next to templates and custom elements. It enables us to encapsulate our style rules from other global styles on any page. After the initialization, we add our template and default styling to our shadow DOM. Finally, we add a click listener to our button to trigger the highlightSelection and highlightRange functions, that are responsible for creating our highlight template around the selected text of the user.

Next to just creating the highlighter, our custom element also needs to set the position of our marker dynamically. For that, we defined our dynamic style function which should be updated whenever a user selects text. To make this work we will define an attribute, that will be passed by our content.js file as a JSON string. We can listen for attribute changes as soon as we return our attribute in the static observedAttributes function. Now we can listen for changes within the attributeChangeCallback to dynamically set the position and visibility of our text highlighter.

Our Web Component is ready to be used. Now we only need to create it with the help of the content.js file.

Creating the Web Component

We finished the setup of our Web Component so let’s quickly test it by adding this to our content.js file:

Since we added our medium-highlighter.js to the manifest.json we can use our custom element within our content.js file. We create an element of it and pass the markerPositionattribute as a JSON string. We set the position and visibility to make the element appear in the upper left corner of the screen.

Adding polyfills

We can now refresh the extension and navigate to any page. We assumed that our marker would appear. Unfortunately, it doesn´t. You will find the reason in the console of your developer tools.

Uncaught TypeError
Uncaught TypeError

Earlier I mentioned, that our content scripts are living in an isolated world. Until now this environment is not ready to use custom elements. Luckily there are polyfills to fix this issue. We just need to include them in our manifest.json :

You can install them by running npm install @webcomponents/custom-elements . In case you haven´t created a package environment yet run npm init --yes in advance.

After that, we should finally see our marker icon in the upper left corner of the screen after refreshing the extension.

Finally set the position of the marker

Since we don´t want the marker to stick in the corner we will set its position dynamically depending on a user´s text selection. Let’s add this code to our content.js file and remove the initial setMarkerPosition function of our demo:

Whenever the user clicks and there is a current text selection we want to show the marker at this specific position. To calculate the position we created the function getMarkerPosition . Next to creating the marker we also have to make sure that we will hide it again. For that, we create a selectionChange event to hide the marker when there is no text selected.

Refresh the extension and enjoy your very own text highlighter :)

You can find the Github repository here.

About the Author

I am a Software Engineering Analyst at Accenture Song. What drives me the most is my urge to create something that has the potential to be helpful and life-changing for others.

I write about Side-Projects, Typescript, Web Components, and many more topics. Check out my Blog on Medium.

Get in touch with me via LinkedIn or follow me on Twitter.