I really like the concept of browser extensions. Especially for Chrome. Surprisingly, though, I never remember Chrome Extensions being a hot topic. Why? If you’re front-end developer like I am, chances are that you spend most of your time in the browser. Why not to fix a thing or two?
What’s more interesting, writing extensions is not that difficult. In common scenario it’s manifest and JS file with logic. Just another JavaScript application.
Let’s take the simplest one: content script. Content script is JS file that will be injected into pages you specify in manifest file. Despite simplicity, it’s very powerful tool. E.g. I slightly made my life easier by injecting content script into our bug tracker. Whenever I open automated error report, it looks for signs that likely to be related to the error (like failed XmlHTTPRequest’s) and emphasises them. It also hides stuff that unlikely to be the cause (e.g. browser version, unless it’s IE). And few more tricks like decompressing stack traces and JSONs, putting related data into a table, highlighting URL components that are more important than others, etc. All the stuff that I previously had to do in mind.
Anyway, let’s see how HelloWorld extension would look like. As I promised, it’s just 2 files.
manifest.json is the first:
1 2 3 4 5 6 7 8 9 10 |
{ "name": "Hello world", "description": "Do you really need a description for that?", "version": "0.1", "manifest_version": 2, "content_scripts": [{ "matches": [ "http://www.html5rocks.com/*" ], "js": ["helloWorld.js"] }] } |
It describes boring stuff like extension version, name and description, and more interesting one, like array of JS files to inject to a page («js») and array of target URL’s («matches»). This manifest will inject helloWorld.js into all pages from html5rocks.com.
The second file, helloWorld.js, is content script itself, which contains immortal
1 |
console.log("Hello world"); |
Extension is ready. Installation is also simple:
- open Google Chrome
- navigate to chrome://extensions
- ensure «Developer mode» checkbox is ON (it usually is)
- press «Load unpacked extension»
- select folder that contains extension
That’s it. If your manifest has any issues, Chrome is polite enough to highlight them. Adding debugger; to content script works just fine. If you added some changes to the content script and wish to see them in action, chrome://extensions page refresh is required before they take an effect (ctrl+R or cmd+R will do).
There’s one problem though. That extension is entirely useless. Let’s try something more real.
Please note, that following example was initially intended for Russian speaking audience. The code is universal, but screenshots might be slightly confusing.
There’s certain web site with great audience, that turns comments section of every single news about Apple/Android into a little branch of hell. That hurts my feelings 🙁 Extension I want to build should fix that. I mean comments, not feelings or audience. Whenever news topic mentions one of ‘banned’ words like Apple, iOS, Android or whatever, each and every comment will be replaced with neutral placeholder image. We can take helloWorld extension above as the base.
Manifest remains almost the same, except for different URL mask:
1 |
"matches": ["http://tech.onliner.by/*"], |
After content script is loaded, we need to find out whether the page is ‘the one’ or not. Apparently, all news titles of that web site fall into this CSS selector: section h3
Screenshot news title, in fact, says «Android peeing on Apple has been found on Google maps». You can imagine what’s happening in the comments.
Comments can be found by this selector: .b-best-comment, .commentListItem
Finally, there’s number of online services that provide placeholder image with certain dimensions specified in URL. I chose placehold.it, which gives grey rectangles. But there’re also services with beer, boobs and kittens.
So, how this should work. After we injected script to the page:
- find title node
- test it for banned words
- select all DOM nodes with comments
- replace their content with appropriately sized image.
That’s the plan. Here’s how it looks in the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
var titleNode = document.querySelector("article h3"), title = titleNode && titleNode.textContent.trim().toLowerCase(), bannedWords = ["Apple", "iPad", "iOS", "Android", "Samsung"], mentionedInTheTitle = function (bannedWord) { return title.indexOf(bannedWord.toLowerCase()) !== -1; }; if (title && bannedWords.some(mentionedInTheTitle)) { var comments = document.querySelectorAll(".b-best-comment, .commentListItem"); Array.prototype.forEach.call(comments, function (commentNode) { var width = commentNode.offsetWidth, height = commentNode.offsetHeight; commentNode.style.width = width + "px"; commentNode.style.height = height + "px"; commentNode.style.paddingLeft = "0"; //http://placehold.it/{0}x{1} //http://placekitten.com/g/{0}/{1} //http://worksafe.placeboobs.com/{0}/{1} //http://placeboobs.com/{0}/{1} commentNode.innerHTML = "<img src='http://placehold.it/{0}x{1}'>" .replace("{0}", width) .replace("{1}", height); }); } else { console.log("The page is safe. So far."); } |
That’s it! Now I can read those articles without disturbing my fragile mind. This how comments section looks now: