Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Building a Chrome Extension in Five Steps

DZone 's Guide to

Building a Chrome Extension in Five Steps

Build it, test it, release it — make a Chrome extension in five easy steps.

· Web Dev Zone ·
Free Resource

chrome-steering-wheel

Make easy-to-use Chrome extensions in a few simple steps

First of all, why build a Chrome extension?

With a Chrome extension, you can customize your browsing experience. Some popular Chrome extensions are StayFocused (the one that blocks out distracting websites) or AdBlock Plus (the one that blocks display ads).

Needless to say, chrome extensions work only on Chrome browsers. Check out the Chrome web store. It boasts of hosting 200,000 extensions as of mid-2019.

You may also like: 17 Chrome Extensions for Web Developers and Designers.

In this blog, I'll explain how to build an extension with an example. The extensions are simple to make and are written in plain old HTML, CSS, and JavaScript. Let's get started!

What Are We Building Today?

We are making an extension that scans the URLs on a page and shows the number of outgoing links to different domains.

To make things complicated, let's open the domain that has the maximum links in a new tab. The end result will look something like this.

End result of URLCount extension

End result of URLCount extension

The above extension shows that the current page has 423 outgoing links to developer.chrome.com, six outgoing links to stackoverflow.com, etc.

What is the use of such an extension?

Nothing. Really. I just want to show you how various pieces work together so that you can build more meaningful extensions yourself.

Extension Architecture

Before we start typing the code, let me explain the various pieces that make up a function. With this understanding, you'll be able to get a sense of how any extension works.

Here is the full code in case you want to skip the blog and cut to the chase. Go to the end of this blog to see how to load and run the extension.

A Chrome extension typically has three scripts. Each script lives in its own world and is not aware of other scripts.

Background script (background.js): Acts a listener of browser events. It has access to chrome extension APIs to do things like creating a new tab, and it can listen to browser events like closing a tab.

Content script (content.js): This has access to the page rendered in the browser. If you want to manipulate the DOM of the page like highlighting the anchor tags with a different color, you will use this script.

UI script(popup.js): An extension can have its own UI, and the UI script has access to this UI.

UI, content and background scripts
UI, content, and background scripts

If the scripts live in their own contexts, how do they talk to each other, you ask?

Answer: Message Passing. 


A message can contain a valid JSON object.

Let's Apply the Theory to our Extension

  1. Our extension has a UI that shows all the outgoing links and the number of such outgoing links per domain. So, we'll have an HTML file, say popup.html.
  2. Popup.html needs a popup.js companion to add the domain names to the popup.html DOM dynamically.
  3. Content.js is required to read the currently active page and fetch all the hrefs in the page. content.js will do so after getting a message from popup.js to fetch URLs.
  4. Once content.js fetches all the URLs, it will pass the data to popup.js through a message.
  5. Popup.js sends the domain with the maximum number of links to background.js through, you guessed it, a message.
  6. Background.js uses a chrome API to open a new tab with the domain name passed form popup.js.

File Structure

The extension will have following file structure. Follow the steps below to create them one by one or download the code here.

URLCount
|- manifest.json
|- icon16.png
|- icon48.png
|- icon128.png
|- popup.html
|- popup.js
|- jquery-3.4.1.min.js
|- background.js
|- content.js


Create a folder called URLCount. Let's put all our files in this folder.

Step One: Manifest.json

Create a file called manifest.json and put the following code in it.

//manifest.json

{
"manifest_version": 2,
"name": "URLCount",
"description": "Sample extension to show URL count in a page",
"version": "1.0"
}


Manifest.json file holds information about the extension like the name of the extension, version number, permissions required by the extension, etc.

Step Two: Add icons

Our extension has an icon associated with it. Let's add an icon to the URLCount folder.

Extension icon next to URL bar. This acts as a trigger.
Extension icon next to URL bar

Chrome's developer guide recommends adding the same icon in 16px, 48px and 128px resolutions. Go ahead and grab an icon that is available in these three resolutions.


Update the manifest.json file about these icons.
//manifest.json

{
"manifest_version": 2,
"name": "URLCount",
"description": "Sample extension to show URL count in a page",
"version": "1.0",
"icons": {
 "128": "icon128.png",
 "48": "icon48.png",
 "16": "icon16.png"
 }
}

Step Three: Extension UI

Now, it’s time to create the UI of the extension - popup.html.

Extension UI
Extension UI

Of course, we have to put this file in manifest.json. We want to show this UI upon clicking the icon. This type of extension is called browser action. There is another type of extension called page action extension. We'll discuss it some other time.

//manifest.json

{
"manifest_version": 2,
"name": "URLCount",
"description": "Sample extension to show URL count in a page",
"version": "1.0",
"icons": {
 "128": "icon128.png",
 "48": "icon48.png",
 "16": "icon16.png"
},

"browser_action": {
 "default_icon": "icon16.png",
 "default_popup": "popup.html"
 }
}


From the UI, you can see that you'll just need a table in the HTML. Let’s also add an id to table called tabs_table. popup.js will use this id to populate the table. Create popup.html and put the code below.

<!DOCTYPE html>
<html>
<head>
   <title>URLCount</title>
</head>
<body>
   <table id=”tabs_table”>
       <tbody>
           <tr></tr>
       </tbody>
   </table>
</body>
</html>


Since the domains will be added to the HTML dynamically, we'll need a popup.js to do so. Let's use jquery to make our life easy. Download the latest jquery min file and put it in the same folder. Update the HTML like so.

<head>
   <title>URLCount</title>
   <script src="jquery-3.4.1.min.js"></script>
   <script src="popup.js"></script>
</head>


The point to note here is we will not add popup.js to the manifest.json.

The first task of popup.js is to send a message to the content script to fetch the domains. Create the file popup.js and put the following code in it.

//popup.js

$(function() {
 // Send a message to content.js to fetch all the top domains
 chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
   var activeTab = tabs[0];
   chrome.tabs.sendMessage(activeTab.id, {"message": "fetch_top_domains"});
 });
});


  1. Chrome.tabs API is used to create, close or rearrange tabs. chrome.tabs.query is an asynchronous API that returns all the tabs that match the given properties. We are looking for the active tab in the current window.
  2. Chrome.tabs.sendMessage sends any data to the selected tab. Let's send "fetch_top_domains" message to the content script to trigger collection of the URLs.

We are not entirely done with the popup.js yet. Let's first see how the fetch_top_domains is handled by the content script.

Step Four: Content Script

Our content script should listen for the fetch_top_domains message. After handling fetch_top_domains, it should send a message to the UI script that it has finished its job. Let's call this second message all_urls_fetched.

Before we write the content.js, let's update the manifest.json.

//manifest.json

"content_scripts": [
 {
 "matches": ["<all_urls>"],
 "js": ["content.js"]
 }
]


The "matches" expression tells Chrome to load content.js for all pages. You can make the content.js to run on twitter like so: "matches": ["https://twitter.com/*"]

After updating manifest.json, create content.js in the same folder and put the following code.

//content.js

chrome.runtime.onMessage.addListener(
 function(request, sender, sendResponse) {
   if( request.message === "fetch_top_domains" ) {
// Handle the message
chrome.runtime.sendMessage({"message": "all_urls_fetched"});
    }
  }
);


chrome.runtime.sendMessage sends a single message to event listeners.

Along with all_urls_fetched message, the content script should also send a payload. The payload should contain all domains and the number of references to each domain.

Let's use document.links to fetch all hrefs from the page and parse them. The final code would look like this.

// content.js

chrome.runtime.onMessage.addListener(
 function(request, sender, sendResponse) {
   if( request.message === "fetch_top_domains" ) {
     var urlHash = {}, links = document.links;
     for(var i=0; i<links.length; i++) {
       var domain = links[i].href.split('/')[2]
       if (urlHash[domain]) {
         urlHash[domain] = urlHash[domain] + 1;
       }
       else {
         urlHash[domain] = 1;
       }
     }
     chrome.runtime.sendMessage({"message": "all_urls_fetched", "data": urlHash});
   }
 }
);


Now, it's time to handle all_urls_fetched in popup.js.

//popup.js

chrome.runtime.onMessage.addListener(
 function(request, sender, sendResponse) {
   if( request.message === "all_urls_fetched" ) {
   }
  }
);


Add logic to parse urlHash sent by content.js and find the domain that has maximum references. Also, add the contents of urlHash to tabs_table in popup.html.

//popup.js

if( request.message === "all_urls_fetched" ) {
 var urlWithMaxLinks;
 var maxLinks = 0;

 for ( var key in request.data ) {
   if(request.data.hasOwnProperty(key)) {
     $('#tabs_table tr:last').after('<tr><td>' + key + '</td>' + '<td>' + request.data[key] +'</td></tr>');

     if(request.data[key] > maxLinks) {
       maxLinks = request.data[key];
       urlWithMaxLinks = key;
     }
   }
 }
}


We are almost there. How will we open urlWithMaxLinks in a new tab?

Enter the background script. Let's send a message from popup.js to chrome runtime with urlWithMaxLinks as payload.

if ( maxLinks != 0 ) {
 chrome.runtime.sendMessage({"message": "open_max_url", "url": urlWithMaxLinks});
}


Step Five: Background Script

It's time to update the manifest.json with information on background.js. Since we'll be opening a new tab with urlWithMaxLinks, let's give tabs permission.

//manifest.json

"permissions": ["tabs"],
"background": {
 "scripts": ["background.js"]
}

background.js listens to open_max_url and uses chrome.tabs API to create a new tab with the URL in the payload. chrome.tabs.create does not work without http:// or https:// (remember, we removed it in popup.js). Let's add an http:// for simplicity before create is called.

//background.js

chrome.runtime.onMessage.addListener(
 function(request, sender, sendResponse) {
   if( request.message === "open_max_url" ) {
     fullURL = "http://" + request.url;
     chrome.tabs.create({"url": fullURL, "active": false});
   }
 }
);


Testing the Extension

It's time to load our extension and test.

  1. Go to chrome://extensions.
  2. Enable "Developer mode."
  3. Click on "Load unpacked."
  4. Select your extension folder.

How to load a chrome extension?

How to load a chrome extension

How to load a Chrome extension?

Please check out the chrome extension developer guide for in-depth information on the complete extension architecture, best coding practices and learn how to distribute your chrome extension on the chrome web store.


Related Articles

Topics:
chrome browser ,developer ,developer tools ,extensions ,web dev ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}