Colorpeek, Part 2: Building Your First Chrome Extension

Avatar of Chris Coyier
Chris Coyier on

The following is a guest post by Tyler Sticka. Tyler created a tool called Colorpeek. Yesterday we looked at the what and why of things. Today we’ll look at how he built the Chrome Extension, which will serve as a great tutorial on how you can get started building your own Chrome Extensions.

In Part One I showed off Colorpeek, a web app I made for quickly sharing CSS colors, and its Chrome extension, which made receiving colors just as easy. So how’d a guy who describes himself as a designer first and a developer second manage to build a fancy-schmancy extension all by himself?

(Apologies to IKEA.)

Pretty easily, it turns out. Chrome extensions are mostly JavaScript, and relatively simple JavaScript at that. Spelunking through all that documentation, may be harrowing at first, but once you’ve mastered the basics, you’ll find that Chrome development is just web development with a few unique twists.

In this post we’ll build our own miniature version of the Colorpeek extension: background-colorpeek, a tool for finding and previewing all the background-color values that a webpage uses. You’ll be surprised how little rocket science is involved.

What You’ll Need

  • Google Chrome.
  • Your favorite text editor.
  • A folder where the extension files will live.
  • An icon for the browser action, which is Chrome-speak for a button in the browser toolbar. This should be a 19 square pixel PNG. You can use this one if you’d like:

You can create all the files we’ll need now or as we move along… it’s really up to you. By the time we finish, our folder will look something like this:

  • event.js
  • icon19.png
  • inject.js
  • manifest.json

Ready? Let’s get started!

The Manifest File

Create a new file at the root of your folder and name it manifest.json. The manifest is the heart of any Chrome extension. It tells Chrome “here’s what I am, and here’s what I need to work.”

Let’s add some details to the file using JSON:

{
  "manifest_version": 2,

  "name": "background-colorpeek",
  "version": "1.0",
  "description": "Get every background-color from the current tab.",

  "browser_action": {
    "default_icon": "icon19.png"
  },

  "background": {
    "scripts": ["event.js"],
    "persistent": false
  },
  
  "permissions": ["<all_urls>"]
}

Most of these properties are self-explanatory, with a few exceptions:

  • manifest_version: This lets Chrome know what version of the manifest file format we are using. Version 1 was deprecated as of Chrome 18, so you should always use 2.
  • background: Information about the scripts our extension requires to respond to things like a click of the browser action. More on this later.
  • permissions: Because we want our extension to grab the background-color values from any URL, we need permission to interact with &lt;all_urls&gt;. If we wanted our extension to only work on https://css-tricks.com/, we could specify that instead.

There’s a lot more you can do with the manifest file, but that’s all our extension will need.

Script Gets Real

You’ll notice our manifest references a JavaScript file we haven’t created yet, event.js. Create it now, and let’s add some code to it:

// This function will eventually contain some logic
// for receiving background-color values from the
// current tab.
function getBgColors (tab) {
  // But for now, let's just make sure what we have so
  // far is working as expected.
  alert('The browser action was clicked! Yay!');
}

// When the browser action is clicked, call the
// getBgColors function.
chrome.browserAction.onClicked.addListener(getBgColors);

This script is what’s known by Chrome (somewhat confusingly) as an event page. This means it will only run when an event the extension cares about occurs… in this case, the browser action being clicked.

Before we continue, we should load our extension in Chrome:

  1. Open up chrome://extensions/ (or click the rightmost menu button, then “Tools,” then “Extensions”).
  2. Make sure the “Developer mode” box in the upper-right is checked.
  3. Click “Load unpacked extension…” and select the folder you created earlier.

If all goes well, the extension should install like any other. Give the browser action (our icon) a click to see a wondrous alert:

Alert: The browser action was clicked! Yay!

Neat, huh?

Tab Talkin’

Now that our browser action is prepared to do our bidding, we need to retrieve CSS information from the current tab. To do that, we should understand a little about how Chrome extensions work relative to webpages loaded in tabs, and how the two can communicate.

Chrome was the first browser to popularize something we take for granted in desktop browsers today: The multi-process architecture. In Chrome, every webpage, add-on and extension gets its own process. This makes it really difficult for a single webpage or extension to crash your entire browser. But it also makes our event page a bit of an island… how do we act on the contents of a tab when that tab’s process is completely separate from event.js?

Bad news: We can’t.

Good news: We don’t need to, because Chrome supports passing messages between scripts. We already have our event page, which we can use to send messages. All we need now is a script in the current tab that can receive them!

Author’s rendition of sendMessage in action.

Here’s how our nifty message-passing will work:

  1. When a user clicks the browser action, event.js will inject a new script into the current tab with instructions on what to do next.
  2. The injected script will do whatever it needs to, responding with the data we requested.

This means our extension will need one more file, inject.js:

// This helps avoid conflicts in case we inject 
// this script on the same page multiple times
// without reloading.
var injected = injected || (function(){

  // An object that will contain the "methods"
  // we can use from our event script.
  var methods = {};

  // This method will eventually return
  // background colors from the current page.
  methods.getBgColors = function(){
    var nodes = document.querySelectorAll('*');
    return nodes.length;
  };

  // This tells the script to listen for
  // messages from our extension.
  chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
    var data = {};
    // If the method the extension has requested
    // exists, call it and assign its response
    // to data.
    if (methods.hasOwnProperty(request.method))
      data = methods[request.method]();
    // Send the response back to our extension.
    sendResponse({ data: data });
    return true;
  });

  return true;
})();

Now we have a script that’s ready to receive commands and return data. Let’s update event.js to take advantage of that:

// Execute the inject.js in a tab and call a method,
// passing the result to a callback function.
function injectedMethod (tab, method, callback) {
  chrome.tabs.executeScript(tab.id, { file: 'inject.js' }, function(){
    chrome.tabs.sendMessage(tab.id, { method: method }, callback);
  });
}

function getBgColors (tab) {
  // When we get a result back from the getBgColors
  // method, alert the data
  injectedMethod(tab, 'getBgColors', function (response) {
    alert('Elements in tab: ' + response.data);
    return true;
  });
}

// When the browser action is clicked, call the
// getBgColors function.
chrome.browserAction.onClicked.addListener(getBgColors);

Reload the extension from chrome://extensions/ and try the browser action on any webpage. You should see a message with the number of HTML nodes therein, which means we’ve successfully interacted with the tab! Woo-hoo!

Finally Doing Something Cool

All the pieces are in place. It’s time to actually do what we set out to do:

  1. When the browser action is clicked, determine all the background-color values of the current tab.
  2. Build a Colorpeek URL based on those values.
  3. Open that URL in a new tab.

There’s very little magic from this point forward… it’s really just JavaScript (and not even fancy jQuery). Here we go…

Open inject.js again and update the getBgColors method:

// Return all of the background-color values
methods.getBgColors = function(){
  // Stores the colors and the number of occurrences
  var colors = {};
  // Get all the nodes on a page
  var nodes = document.querySelectorAll('*');
  // Instantiate variables we'll use later
  var node, nodeArea, bgColor, i;

  // Loop through all the nodes
  for (i = 0; i < nodes.length; i++) {
    // The current node
    node = nodes[i];
    // The area in pixels occupied by the element
    nodeArea = node.clientWidth * node.clientHeight;
    // The computed background-color value
    bgColor = window.getComputedStyle(node)['background-color'];
    // Strip spaces from the color for succinctness
    bgColor = bgColor.replace(/ /g, '');
    // If the color is not white or fully transparent...
    if (
      bgColor != 'rgb(255,255,255)' &&
      !(bgColor.indexOf('rgba') === 0 && bgColor.substr(-3) === ',0)')
    ) {
      // ...set or override it in the colors object,
      // adding the current element area to the
      // existing value.
      colors[bgColor] = (colors[bgColor] >> 0) + nodeArea;
    }
  }

  // Sort and return the colors by
  // total area descending
  return Object.getOwnPropertyNames(colors).sort(function (a, b) {
    return colors[b] - colors[a];
  });
}

We’re almost done! Now update the getBgColors function in event.js:

// Get background-color values from the current tab
// and open them in Colorpeek.
function getBgColors (tab) {
  injectedMethod(tab, 'getBgColors', function (response) {
    var colors = response.data;
    if (colors && colors.length) {
      var url = 'http://colorpeek.com/#' + colors.join(',');
      chrome.tabs.create({ url: url });
    } else {
      alert('No background colors were found! :(');
    }
    return true;
  })
}

Reload the extension and give it a try. If it works, go get yourself a beverage to celebrate your new-found ability to make Chrome extensions!

Some of the background-color values used on CSS-Tricks.

Extra Credit

This extension is just the tip of the iceberg. There are a ton of things you can do with Chrome’s Platform APIs. Here’s some stuff to try if your brain still thirsts for knowledge:

I hope this helped demystify some aspects of Chrome extension development. And if you like what we built together, please give Colorpeek for Chrome a try… I think you’ll dig it. Happy tinkering!