Changing the Web, One Puppy Image at a Time
Let’s talk about browser extensions, or more specifically how to build your first Chrome extension. I went to a class a few weeks ago where we had a lot of fun creating this very silly extension with a few lines of code. I thought I would share it, so you all can try it out and maybe even expand on it.
What is a Browser Extension
Before we get started, let’s review what browser extensions are. Browser extensions are really just short pieces of code that provide some extra functionality to a browser. You’re probably using a few right now such as an Ad Blocker or a Password Manager. Google has a whole store of really useful and fun Chrome extensions that people have built to enhance the web browsing experience.
https://chrome.google.com/webstore/category/extensions
For today, we’ll be creating an extension that targets all images on a webpage, and replaces them with pictures of puppies!
Folder Structure
To get started:
Clone the following repo locally to grab the starter files: https://github.com/himashi99/chrome-extension
Your folder structure should resemble the above.
I’ve included the “poop” emoji for you in the image folder, but feel free to use 3. any image icon you would like.
The Manifest File is a JSON file that describes our extension to Chrome. It is where we specify important information about our extension, such as permissions it needs or icons it uses.
Include the below code in your “manifest.json” file.
{
"name": "Yay Chrome!",
"version": "1.2",
"description": "First project to build a fresh chrome extension",
"manifest_version": 2,
"permissions": ["activeTab", "declarativeContent"],
"icons": {
"16": "images/poop_16.png",
"32": "images/poop_32.png",
"48": "images/poop_48.png",
"128": "images/poop_128.png"
},
"page_action": {
"default_icon": {
"16": "images/poop_16.png",
"32": "images/poop_32.png",
"48": "images/poop_48.png",
"128": "images/poop_128.png"
}
},
"background": {
"scripts": ["scripts.js"],
"persistent": false
}
}
Let’s break down the above code:
We need to give our extension a name, version number, and a short description.
The current manifest_versions is 2 so leave this as is.
The “activeTab” permission allows access to the current tab the user is on. The “declarativeContent” permission allows the extension to be aware when a tab is changed, or a new webpage is visited.
We import image icons of varying sizes so our extension is responsive on different screen sizes.
The “pageAction” places the icon in the Chrome toolbar and represents an action that can be taken on a current webpage i.e. button is clickable.
In the final code block, background scripts are loaded when needed and listens for, and manages events. Persistent is set to “false” so that background scripts are unloaded once they have completed their action.
In this case, the Manifest file instructs that “scripts.js” file should be loaded. We will be coming back to “scripts.js” and “main.js” files shortly.
Chrome Setup
Open up Chrome and go into the Extension Manager
We need to let Chrome know we’re badass developers, so let’s turn on the “Developer Mode” at the top right corner. This will allow us to load our own extensions.
Click on “Load Unpacked” and select your “chrome-extension” folder.
Alright! We have now loaded our extension in. You should see the extension in the extension manager, and the icon in the Google Chrome Toolbar. The icon will be greyed out and inactive.
If you do make any changes to the JSON file at this point, you can hit the refresh button on your extension.
scripts.js File
It’s time to activate the icon , and in order to do that, we need to link the button to our “scripts.js” file.
- Include the below code blocks in your “scripts.js” file.
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([{
conditions: [new chrome.declarativeContent.PageStateMatcher({
pageUrl: {schemes: ['https', 'http']},
})
],
actions: [new chrome.declarativeContent.ShowPageAction()]
}]);
});
chrome.pageAction.onClicked.addListener(function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.executeScript(
tabs[0].id,
{file: 'main.js'}
);
});
});
Let’s break down the above code:
First Code Block:
Going back to the Manifest File for a second, remember that we specified permissions for activeTab and declarativeContent? Well this lets us use the Chrome APIs (“chrome.declarativeContent”), which then allows our extension to take actions depending on the content of a webpage, without asking for host permission to read the webpage content.
We also defined “pageAction” in the Manifest File as our extension icon in the Chrome toolbar.
The declarative API further allows rules to be set on the “onPageChanged” event, which then takes an action when conditions under “PageStateMatcher” are met.
The “PageStateMatcher” only matches webpages when the listed conditions are met. In this case, the rules would show a page action for any http or https webpage.
“removeRules” is called to clear all previously defined rules that were added when the extension was installed, so a new set of rules can be defined (“addRules”).
Second Code Block:
When pageAction, which is the extension icon we created in in our Manifest file is clicked, we add a listener i.e. don’t run the script yet.
The next line refers to the “active tab” permission we stated in our Manifest File. Chrome queries to the current window that is open, and determines the active tab. Once the active tab is determined, it runs a function with the parameters “tabs”.
The last bit of code tells Chrome to go to the active tab, which we isolated in the previous line, and execute a script, in this case the “main.js” file.
Save your file and refresh your extension.
Open a new tab and you should see that your icon is now active and no longer greyed out.
We’re finally done all the setup work and can get to the fun part!
Image Placeholder Service
We need to find some replacement images for our extension. There are a couple of sites that do this and have images of varying sizes. I’m going to use the puppy one, https://placedog.net/ but there are many more so choose your favourite!
main.js File
We’re now going to write our script so we can replace the images with our cute puppy photos when we click on our extension.
- Include the below code in your main.js file
var allImages = document.getElementsByTagName('img');
for (var i = 0; i < allImages.length; i++) {
allImages[i].addEventListener('mouseover', function() {
var thisImageHeight = this.clientHeight;
var thisImageWidth = this.clientWidth;
this.setAttribute('src', 'https://placedog.net/' + thisImageHeight + '/' + thisImageWidth)
})
}
Let’s break down the above code:
We want to target all of the images on a given webpage. Since these image elements will all have an “img” tag, we grab them with document.getElements byTagName(‘img’) and assign it to the variable “allImages”.
We can now use a “for loop” to loop over each image and add an Event Listener. In this case, we are awaiting the user to mouse over the image.
The width and height of our new images should be equal to the images that are being replaced. We assign this original image height and width to separate variables: thisImageHeight and thisImageWidth.
We can now use the setAttribute feature to change the attribute of the src element. Remember that “this” refers to the image that was moused over. We will also include the image height and width using the variables from our previous line.
You did it!
Alright, save your file and refresh your extension. Open up a webpage (with lots of images) and try out your fun new Chrome extension!
Top comments (8)
Just a note on WebExtension: it's an API from W3C so this code may work on Firefox, Edge, Opera etc :)
Oh good to know! I'll need to try it out.
My first (and only to date) chrome extension is a find and replace text extension. Makes reading the news much more entertaining! :)
Haha that sounds great!
I like your example too! From the sounds of it, combining yours to replace images, and mine to replace text would effectively rewrite the internet's content! ;)
Thank you for this clear how-to (and for PUPPIIIEEEESSSS 🐕🐩🐾)! This is the first article that's made this process seem fun 💯
Very clear. Thanks for writing this!
I'm glad you liked it!