BlogsDope image BlogsDope

How To Make A Chrome Extension Using JavaScript?

Dec. 15, 2020 JAVASCRIPT BROWSER 13346

Most of us use different types of chrome extensions, and as a programmer, you must have sometimes wondered how they work or how to create one. Well, it is not difficult at all. You can build a basic Chrome extension in no time if you know HTML, CSS, and JavaScript. Advanced extensions can get complex depending upon your logic. However, in this tutorial, we are going to create simple to intermediate level extensions. So, we can focus on fundamental concepts. Once you know the basics, you can create an advanced one as well.

So, what is an extension? It is a small software program that allows you to customize your Chrome browser experience and add functionalities according to your needs and preferences. Before moving ahead, let’s discuss the types of extensions. A chrome extension can be of type Browser Action, Page Action, or neither. Both browser action and page action extensions get displayed in the Chrome toolbar. However, a browser action extension is available on all pages, unlike a page action extension, which is only available on some pages, as specified by the programmer.

Browser Action Extension


Let’s get started now and create a browser action extension. Create a separate folder for this project. Include popup.html, main.js, style.css, and manifest.json for now. The manifest.json file contains the metadata about the extension, such as name, description, permissions, etc., in the JSON format.

  • manifest.json
{
  "manifest_version": 2,
  "name": "My Todo List",
  "version": "1.0",
  "description": "A plain text description",
  "icons": {
    "128": "icon128.png",
    "48": "icon48.png",
    "16": "icon16.png"
  },

  "browser_action": {
    "popup": "popup.html",
    "default_popup": "popup.html"
  }
}

The first three fields are required. You are not supposed to change the manifest_version field. The "name" field contains the name of your extension. Since this is the first version, we keep it as 1.0. The "icons" field takes an object, which contains icons of different sizes to display on the toolbar. It is not necessary to include multiple sizes, but Chrome recommends it so it can select the best one, according to the need. Since we are creating a browser action extension, we add this as a field, which contains a popup, an HTML file that shows up when the user clicks on the icon.

To-Do List Extension


In this example, we create a simple To-Do List, in which a user enters the task in a form. Upon submission, it gets added to a list. Moreover, clicking on the list marks it as complete. The code for popup.html, main.js, and style.js is as follows.

  • style.css
  • popup.html
  • main.js
body {
  background: black;
  color:white;
  width:500px;
  height:500px;
  margin:auto;
  padding:5%;
}
html {
  width:500px;
  height:500px;

}

input {
  width: 100%;
  padding: 10px 0;
  font-size: 16px;
  color: #fff;
  margin-bottom: 30px;
  border: none;
  border-bottom: 1px solid #fff;
  outline: none;
  background: transparent;
}
#add_button {
 padding:0.35em 1.2em;
 border:0.1em solid #FFFFFF;
 margin:0 0.3em 0.3em 0;
 border-radius:0.12em;
 box-sizing: border-box;
 text-decoration:none;
 font-family:'Roboto',sans-serif;
 font-weight:300;
 color:grey;
 text-align:center;
 transition: all 0.2s;
}
#add_button:hover{
 color:#000000;
 background-color:#FFFFFF;
}
/* List */
ul {
  counter-reset: index;  
  padding: 0;
  max-width: 300px;
}

/* List element */
li {
  counter-increment: index; 
  display: flex;
  align-items: center;
  padding: 12px 0;
  box-sizing: border-box;
}


/* Element counter */
li::before {
  content: counters(index, ".", decimal-leading-zero);
  font-size: 1.5rem;
  text-align: right;
  font-weight: bold;
  min-width: 50px;
  padding-right: 12px;
  font-variant-numeric: tabular-nums;
  align-self: flex-start;
  background-image: linear-gradient(to bottom, aquamarine, orangered);
  background-attachment: fixed;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}


/* Element separation */
li + li {
  border-top: 1px solid rgba(255,255,255,0.2);
}
ul li.checked::before {
  content: '';
  border-color: ForestGreen;
  border-style: solid;
  border-width: 0 3px 3px 0;
  min-width: 10px;
  transform: rotate(45deg);
  height: 5px;
  width: 10px;
  padding:2px;
  margin:8px;
}
<!DOCTYPE html>
<html>
<head>
	<title>Simple Extension</title>
	<link rel="stylesheet" type="text/css" href="style.css">
	
</head>
<body>
	<form name="myform">
		<input type="text" name="task" id="task_input" placeholder="Enter your task here" required />
		<button type="submit" id="add_button">Add</button>
	</form>
	<ul id="list_task">
	</ul>
	<script type="text/javascript" src="main.js"></script>
</body>
</html>
// submitting it
form = document.forms.myform;
form.addEventListener("submit", (event) => {
  event.preventDefault();
  list_item = document.getElementById("list_task");
  var list = document.createElement("li");
  var listcontent = document.createTextNode(form.task.value);
  list.appendChild(listcontent);
  list_item.appendChild(list);
  form.task.value = "";
});

// marking it as complete
const ul = document.getElementById("list_task");
ul.addEventListener("click", function (e) {
  e.target.classList.toggle("checked");
});

Uploading the Extension to Chrome


Let’s now load it in the Chrome. Go to chrome://extensions/ and enable the developer mode. Then, click on the Load unpacked button and choose the directory where you have placed the files. And that’s it! You have successfully created your first chrome extension. 

Clicking on the extension icon will pop up the contents of popup.html. Here are some screenshots.

Cool, right?

Chrome Storage API


One limitation you can observe in that extension is that as soon as we close the extension, our data gets lost because we are not storing it anywhere. To do that, we will make use of chrome Storage API. To use it, we need to include it in the permissions field in the manifest.json file.

  • manifest.json
"permissions": [
	"storage"
]

To save our data, we will use the chrome.storage.sync.set() method, which takes the values we want to update as an object, and an optional callback function, as arguments. To retrieve the data, we will use the chrome.storage.sync.get() method. It also takes two arguments. The first optional argument can be a string representing the key, an array of strings representing multiple keys, or an object to specify the default value of the key. If not provided, then the value becomes null, and all the contents of the storage will be retrieved. The second argument is a callback function that has a parameter of the type object having the specified items. Let’s use them in our main.js file.

  • main.js
// get the stored data and display it
chrome.storage.sync.get({ tasks: "" }, (result) => {
  document.getElementById("list_task").innerHTML = result.tasks;
});

// submitting it
form = document.forms.myform;
form.addEventListener("submit", (event) => {
  event.preventDefault();
  list_item = document.getElementById("list_task");
  var list = document.createElement("li");
  var listcontent = document.createTextNode(form.task.value);
  list.appendChild(listcontent);
  list_item.appendChild(list);
  form.task.value = "";

  // save the task
  tasks = document.getElementById("list_task").innerHTML;
  chrome.storage.sync.set({ tasks: tasks }, () => {
    console.log(tasks);
  });
});

// marking it as complete
const ul = document.getElementById("list_task");
ul.addEventListener("click", function (e) {
  e.target.classList.toggle("checked");

  //save it
  tasks = document.getElementById("list_task").innerHTML;
  chrome.storage.sync.set({ tasks: tasks }, () => {
    console.log(tasks);
  });
});

 Closing the extension won’t result in data loss now.

Options Page


In addition to the popup, we can also have an options page that gets open by right-clicking on the icon. Let’s add a reset button on the options page. Clicking on it will remove all the tasks. Let’s create three new files, options.html, options.css, and options.js. Also, we need to update manifest.json by including options_page, which takes an HTML file name, i.e.,

  • options.css
  • options.html
  • manifest.json
  • options.js
#reset {
 width:200px;
 height:50px;
 padding:0.35em 1.2em;
 border:0.1em solid grey;
 margin:0 0.3em 0.3em 0;
 border-radius:0.12em;
 box-sizing: border-box;
 text-decoration:none;
 font-family:'Roboto',sans-serif;
 font-weight:300;
 color:grey;
 font-size:2em;
 text-align:center;
 transition: all 0.2s;
}
#reset:hover{
 color:#000000;
 background-color:grey;
}
<!DOCTYPE html>
<html>
<head>
	<title>Simple Extension</title>
	<link rel="stylesheet" type="text/css" href="options.css">
</head>
<body>
	<button id="reset">Reset</button>
	<script type="text/javascript" src="options.js"></script>
</body>
</html>
{
  "manifest_version": 2,
  "name": "My Todo List",
  "version": "1.0",
  "description": "A plain text description",
  "icons": {
    "128": "icon128.png",
    "48": "icon48.png",
    "16": "icon16.png"
  },

  "browser_action": {
    "popup": "popup.html",
    "default_popup": "popup.html"
  },

  "options_page": "options.html",

  "permissions": ["storage"]
}
document.getElementById("reset").addEventListener("click", () => {
  chrome.storage.sync.set({ tasks: "" }, () => {
    console.log("Data Reset");
  });
});

Here are some screenshots of the output.

Background and Event Pages


Let’s now discuss two more types of pages, background and event pages. These pages run in the background and not when we click on the icon. A background page runs at all times. The event page, on the other hand, only runs when some event is triggered, for example, closing or opening a tab, etc. Since the event pages only run when they are required, they are preferable over background pages. To use background or event pages, we need to set the background field in the manifest.json file. 

"background": {
	"scripts" : ["background.js"],
	"persistent":false
}

If the persistent key is false, then the page is of the type event. Otherwise, the page is of the type background.

Chrome Context Menus API


Let’s now add a new item to Chrome’s context menu using the chrome.contextMenus API. The item will appear on the context menu when we select any content on a web page. Clicking on it will add the selected text to our To-Do list. We will code this functionality in the background.js file. Let’s add this API in the permissions field.

"permissions": [
	"storage",
	"contextMenus"
],

We create an item in the context menu using the chrome.contextMenus.create() method, which takes an object and a callback function. You can specify the properties such as id, title (text to appear in the item), and context (the condition in which it will appear), etc. In our case, context will be selection.

Moreover, we add an event listener, chrome.contextMenus.onClicked.addListener(), that gets called when an item in the context menu is clicked.

The code is given below.

  • background.js
chrome.contextMenus.create({
  id: "addtask",
  title: "Add this to your To DO list",
  contexts: ["selection"],
});

chrome.contextMenus.onClicked.addListener((iteminfo) => {
  //check if the clicked item is the required item
  if (iteminfo.menuItemId == "addtask") {
    var updated_tasks = "";
    //get the task list
    chrome.storage.sync.get({ tasks: [] }, (result) => {
      updated_tasks = result.tasks + "<li>" + iteminfo.selectionText + "</li>";
      // update the task list
      chrome.storage.sync.set({ tasks: updated_tasks }, () => {
        console.log("Data Updated");
      });
    });
  }
});

Output

Page Action Extension


As already mentioned, page action extensions are those extensions, which are applicable to some pages, and not all. For example, the mappy extension finds the addresses on a website and opens up a map window. This extension will only apply to web pages containing addresses and will get grayed out on pages that do not include them.

Content Scripts


Let’s create a simple extension that changes the font size on the google.com page. It is just a sample example to get you started with the page action extension. We will perform the task using Content Scripts. They are files that run in the context of web pages. Using these files, we can get information about the web pages, and manipulate them, etc. However, they cannot access all Chrome APIs, and therefore, we have to do message passing between content scripts and background scripts.

Just like in the above section, we create a new folder and include all the necessary files there. Now, instead of browser_action, we have to add the page_action field. Consider the manifest.json file given below.

  • manifest.json
{
  "manifest_version": 2,
  "name": "Change font size",
  "version": "1.0",
  "description": "Change the size of the font on the google.com page",
  "icons": {
    "128": "icon128.png",
    "48": "icon48.png",
    "16": "icon16.png"
  },

  "page_action": {
    "default_popup": "popup.html"
  },

  "background": {
    "scripts": ["background.js"],
    "persistent": false
  },

  "content_scripts": [
    {
      "js": ["content.js"],
      "css": ["style.css"],
      "matches": ["https://www.google.com/"]
    }
  ],

  "permissions": ["tabs", "https://www.google.com/"]
}

The content_scripts field takes an array of objects. The object contains keys “js” and “css” to inject JavaScript and CSS files, and the key “matches” takes allowed web pages’ URL. We need to include the same URL in the permissions field as well.

Chrome Runtime API and Tabs API The content.js file is given below. In this file, we use chrome.runtime API to send a message of “showPageAction” to the background.js file to activate the icon.

  • contents.js
chrome.runtime.sendMessage({msg:"showPageAction"})

We listen for this message in the background.js file using the chrome.runtime.onMessage.addListener() method. It takes a callback function that has three parameters, message, sender, and a function to send a response. We use the query() method of the chrome.tabs API to get the information of the required tabs. It takes an object (to specify properties) and a callback function (returns an array of Tab result). We retrieve all the tabs that are active and in the current window. Then, in the callback function, we use the chrome.pageAction.show() method to activate the icon. The background.js file is as follows.

  • background.js
chrome.runtime.onMessage.addListener(function (message, sender) {
  if (message.msg == "showPageAction") {
    chrome.tabs.query(
      {
        active: true,
        currentWindow: true,
      },
      (tabs) => {
        chrome.pageAction.show(sender.tab.id);
      }
    );
  }
});

Let's increase the font size of links Gmail and Images. We find its class by inspecting elements and then apply the styling on it.

  • style.css
div.gb_oe.gb_i.gb_Ng.gb_Dg {
  font-size: 3em;
}

Output

The popup.html file is given below.

  • popup.html
<!DOCTYPE html>
<html>
<head>
	<title>Simple Extension</title>	
</head>
<body>
	<h1>simple chrome extension to change the font size</h1>
</body>
</html>

Liked the post?
A computer science student having interest in web development. Well versed in Object Oriented Concepts, and its implementation in various projects. Strong grasp of various data structures and algorithms. Excellent problem solving skills.
Editor's Picks
0 COMMENT

Please login to view or add comment(s).