Photo by Firmbee.com on Unsplash
How to Build a Chrome Extension with Vanilla JavaScript from Scratch
Hello developers,
In this blog post, you will learn how to build a local Chrome extension for personal use from scratch. The extension helps to copy any webpage's URL so you can save for fast access. You can also use as a bookmark to jot things down for quick usage elsewhere.
We have all done something similar, copying links of pages and saving it in Notepad. There's nothing bad in that really, that's even the usefulness of Notepad - to save notes.
But as we save links in Notepad, it gets pretty messed up with all our old notes. This extension will make saving URLs very easy.
Lets begin.
Prerequisite
Basic knowledge of these is required:
We will call this project savelinks, because it is a Chrome extension for saving links. The links will be saved in our browser local storage.
Building
Step 1 - Project set up
Create a folder named savelinks. Inside it, create index.html, style.css and script.js files.
Step 2 - HTML code
Input this code in your index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Savelinks Extension</title>
</head>
<body>
<input type="text" id="input-el">
<button id="input-btn">SAVE LINK</button>
<button id="save-btn">SAVE TAB</button>
<button id="delete-btn">DELETE ALL</button>
<ul id="ul-el"></ul>
<script src="script.js"></script>
</body>
</html>
We have linked our CSS file, created an input tag to create new links to save. We have also created three buttons for saving inputs, saving tab and deleting all saved links respectively. We also have an empty <ul>
element. We will use JavaScript to put all our saved links in this <ul>
element.
Step 3 - CSS code
Input this code in your style.css
body {
margin: 0;
padding: 10px;
font-family: Arial, Helvetica, sans-serif;
min-width: 400px;
}
input {
width: 100%;
padding: 10px;
box-sizing: border-box;
border: 1px solid #5f9341;
margin-bottom: 4px;
}
The CSS code above will set the styles for the extension body, we have also styled the input box. It has a width of 100%, 10px padding and border.
Add the next lines of CSS code.
button {
background: #5f9341;
color: white;
padding: 10px 20px;
border: 1px solid #5f9341;
font-weight: bold;
}
#delete-btn {
background: white;
color: #5f9341;
}
This will style all our 3 buttons while the delete button will have a background color of white with a different text color (hex code = #5f9341).
Add the next lines of CSS code.
ul {
margin-top: 20px;
list-style: none;
padding-left: 0;
}
li {
margin-top: 5px;
}
a {
color: #5f9341;
}
This will style our list by giving it margins, the displayed links by JavaScript will have a color with hex code = #5f9341.
Step 4 - JavaScript code
Now let's add some JavaScript code.
let savedLinks = []
const inputEl = document.getElementById("input-el")
const inputBtn = document.getElementById("input-btn")
const deleteBtn = document.getElementById("delete-btn")
const saveBtn = document.getElementById("tab-btn")
const ulEl = document.getElementById("ul-el")
const linksFromLocalStorage = JSON.parse( localStorage.getItem("savedLinks") )
We have created an empty array called savedLinks, created a variable to hold the input and buttons elements, we are also getting savedLinks item from loacal storage, parsing it into JSON format and saving it into a variable. We haven't initialized a local storage yet, just saving the command in a variable called linksFromLocalStorage.
Let's add more JavaScript code.
if (linksFromLocalStorage) {
savedLinks = linksFromLocalStorage
render(savedLinks)
}
If linksFromLocalStorage exist, that is if we have savedLinks in local storage, render function will be called to display the links. Note that we have also passed savedLinks as an arguments in the render function.
Let's move on, add this code too.
function render(links) {
let listItem = ""
for (let i = 0; i < links.length; i++) {
listItem += `
<li>
<a target='_blank' href='${links[i]}'>
${links[i]}
</a>
</li>
`
}
ulEl.innerHTML = listItem
}
The function above is called render with parameter called links. The function will take in an array as argument. A variable called listItem is defined and has an empty string "" value for now. A for loop
is defined, it will loop through the array passed in let i = 0; i < links.length; i++
and create a <li>
list of each item and add each to listItem. Each <li>
will contain the saved links and the href
also set to the link. The list of links in listItem variable will be embedded inside the <ul>
element with ulEl.innerHTML = listItem
.. This will render the list to our index.html.
Let's add some code.
saveBtn.addEventListener("click", function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
savedLinks.push(tabs[0].url)
localStorage.setItem("savedLinks", JSON.stringify(savedLinks))
render(savedLinks)
})
})
The save button has an addEventListener
method that takes in two parameters here, the type of the event to listen to ("click") and a callback function to call.
The function contains the chrome.tabs
API that is only available when we are running the code in the context of the extension. The API allows us to interacts with the browser's tab system and can be used to create, modify, and rearrange tabs in the browser.
We attached a query()
method to the tabs
object to verify the tab we are working on. The query()
method takes in an object and a function as an argument. The object has two properties, active
and currentWindow
. active
is set to true to specify that we are in the current tab and currentWindow
is set to true to specify that we are in the current window.
The functions takes a tabs parameter and navigate to the tab's url with tabs[0].url
. The tabs is an array that has the first item as an object that contains the url of the current tab. The url property is gotten with tabs[0].url
.
Next is the delete button.
deleteBtn.addEventListener("dblclick", function() {
localStorage.clear()
savedLinks = []
render(savedLinks)
})
On double click, the local storage that contains the saved links is cleared with localStorage.clear()
, the savedLinks
rendering to the page is set to null, render(savedLinks)
is called to run which returns no list of links.
What if we want to put links manually?
Here's the code handling that.
inputBtn.addEventListener("click", function() {
savedLinks.push(inputEl.value)
inputEl.value = ""
localStorage.setItem("savedLinks", JSON.stringify(savedLinks))
render(savedLinks)
})
The savedLinks
array gets the input value pushed to it, then the input box is cleared with inputEl.value = ""
. The savedLinks
array re-saves to the local storage to include the input value. The savedLinks
array is rendered into the browser with the render
function.
Copy the full JavaScript code here
let savedLinks = []
const inputEl = document.getElementById("input-el")
const inputBtn = document.getElementById("input-btn")
const deleteBtn = document.getElementById("delete-btn")
const saveBtn = document.getElementById("tab-btn")
const ulEl = document.getElementById("ul-el")
const linksFromLocalStorage = JSON.parse( localStorage.getItem("savedLinks") )
if (linksFromLocalStorage) {
savedLinks = linksFromLocalStorage
render(savedLinks)
}
function render(links) {
let listItem = ""
for (let i = 0; i < links.length; i++) {
listItem += `
<li>
<a target='_blank' href='${links[i]}'>
${links[i]}
</a>
</li>
`
// SAME
// const li = document.createElement("li")
// li.textContent = savedLinks[i]
// ulEl.append(li)
}
ulEl.innerHTML = listItem
}
saveBtn.addEventListener("click", function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
savedLinks.push(tabs[0].url)
localStorage.setItem("savedLinks", JSON.stringify(savedLinks))
render(savedLinks)
})
})
deleteBtn.addEventListener("dblclick", function() {
localStorage.clear()
savedLinks = []
render(savedLinks)
})
inputBtn.addEventListener("click", function() {
savedLinks.push(inputEl.value)
inputEl.value = ""
localStorage.setItem("savedLinks", JSON.stringify(savedLinks))
render(savedLinks)
})
Step 5 - Setting the meta data
The meta data is the information sent to the Chrome browser about the extension. manifest.json file is used to configure the extension and provide meta data about it. The file is a .json file so it uses the object structure but with "" in the key value pairs. The manifest_version, version, name, action, action are in "" because we are using JSON. Read more about JavaScript JSON here.
{
"manifest_version": 3,
"version": "1.1",
"name": "Savelinks",
"action": {
"default_popup": "index.html",
"default_icon": "link.png"
},
"permissions": [
"tabs"
]
}
manifest_version tells Chrome which version of json we are using, version lets us set the version of our extension, name provides the name of the extension, action provides an objects in json format with two values, the default_popup provides the home page of the index, it is set to the index.html while the default_icon sets the extension icon, there is an icon named link.png, it will display on our Chrome interface when we deploy the extension, the permissions array gives us the permission to access the url so we have declared the "tabs" permission.
Deployment
To deploy our extension, follow these steps:
Click on the Chrome extension icon by the top right icon, your current extensions should pop up, click the Manage extension at the end of the list. The direct link is chrome://extentions
Trigger the developer mode at the top right corner
Navigate to load unpacked at the top right corner
It prompts you to choose your extension project, choose the savelinks folder to deploy.
Open a new tab and check the new extension.
You can now start to use the new extension.
If deployment is successful, the index.html page should pop up and the savelinks extension should work fine.
That will be all. Hope you found any value here as you learn to build more projects effectively.
Before You Leave,
If you enjoyed this article and want to see more content related to JavaScript and Web development, then follow me here, Twitter or connect on LinkedIn.
I'd be happy to count you as one of my ever-growing group of awesome friends on the internet.
If you also want to support me, you can buy a cup of coffee for me here.
Thanks.