Writing My First Chrome Extension - A Facebook News Feed Filter

December 13, 2015

A while back I had an idea for a chrome extension that would block any posts about Kim Kardashian from my News Feed. Often times pop-culture can be akin to a parasite, sucking up people’s attention, thoughts, energy, fears, self-worth, or even dignity just to thrive.

Am I being a bit over dramatic? Yes. Can you tell that I don’t like Kim Kardashian? Yes. But I had a solution that disappointed my inner-Socrates: selective ignorance. Ignore the stuff that I really, truly, never wish to see again, that has no value, and I’ll be better off. Thus, I wrote Blissfully- my first Chrome Extension. Blissfully lets users enter keywords/phrases/#hashtags that they have no interest in seeing appear on their Facebook News Feeds, and then hides them from sight.

This essay/article is just a big summary of what went into this plugin, I hope you enjoy. If you are looking to write your first chrome plugin, feel free to send me an email (found on my homepage), and I can elaborate a little on some things to be aware of. Anyway, here’s my summary:

Design Decisions

Technical Decisions

Why Blissfully is Cool

Challenges

Lessons Learned

Design Decisions

  1. Material Design(ish)

Since this was a Chrome extension, I decided to go with a slightly material design approach (if you download the plugin, notice the button hover and the input field label animations, which are common material design patterns). I’m not sure if I succeeded, but I made a conscious decision to have the button and blacklist to follow a material aesthetic (even the blacklist follows official Google padding guidelines).

  1. Out of Sight, out of Mind

The chrome extension icon only shows up when the user is on Facebook because that’s the only time they will ever use it. The user can also see which topics they are blocking, but only if they explicitly choose to do so (by clicking “view blacklist”). Why am I making special note of this? Because if you have no interest in seeing status updates about a specific person, then why should you see their name every time you open the plugin? This is a mistake a few other Facebook filter plugins made- displaying the blacklist upon clicking the popup.

  1. If They Don’t Notice it Working, Then You’re Doing it Right

Another important design decision I made was to NOT let Blissfully visually indicate on a user’s FB news feed that something has been blocked, and it will not leave a message in place of where the post should have been to let the user know it did its job. That only serves the Ego of the developer, reminding users “Hey I’m working!”, when in reality, a user should be completely oblivious. Instead, Blissfully keeps a counter that will let a user know, when they click on Blissfully, the total number of posts that it has successfully blocked from sight. If I replaced each post in a feed that was about **____** (insert keyword) with “THIS POST IS ABOUT **____**, AND IS THEREFORE BLOCKED”, that would be pretty silly, because the whole point is to avoid having to think of **____** ever again. Cruel, I know, but again, this was another mistake that was made by other FB feed filters.

Technical Decisions

  1. Written in 100% Vanilla JS. I’m trying to get as deep of an understanding of JavaScript as I can, and therefore have not been relying on jQuery or many frameworks at this moment. Plus, as an added benefit, I get to avoid KBs worth of unnecessary bloat, keeping the plugin as light-weight as possible.

  2. Synced Chrome Storage The data that Blissfully stores uses Chrome’s Storage Sync API, allowing users to seamlessly sync their keyword blacklists across multiple Chrome browsers.

  3. Use of the Module Design Pattern I used a module pattern in the content script that publicly exposes a few functions which are called by the popup’s script. This wasn’t necessary, as I could have used Chrome’s messaging API to share values between the script, but I was not aware of those APIs at the time and chose to use this pattern (see ‘Lessons Learned’).

  4. Use of Mutation Observer API I elaborate on this in the following section, but I chose to use the newly added Mutation Observer API (only supported by Chrome and Firefox at this time), which allows the plugin to observe the DOM and run a callback function whenever a new element is added to the DOM. I thought this was pretty cool and later learned that this is also asynchronous, which made my plugin pretty speedy.

Why Blissfully Is Cool

Blissfully is cool because it is simple to use and also one of the more technically efficient and light-weight Facebook news feed filter plugins that I’ve found on the Chrome store at this time. While other plugins rely on jQuery, Blissfully is Vanilla, saving a few extra kilobytes. I know this doesn’t really make much of a difference, and it wasn’t the reason why I wrote it in Vanilla JS, but it does feel good when you avoid unnecessary bloat.

While other plugins search the entire DOM for a keyword each time a search function is called, or search FB’s DOM on each mouse scroll, Blissfully only searches the elements on a page that were added to the DOM rather than scan the entire DOM tree each time or scanning the DOM on every single page scroll despite the DOM not having changed. It does this by using a browser API called Mutation Observer. I hope I haven’t misspoken by saying that this plugin is more efficient because it uses Mutation Observer, but based on my current understanding of how Mutation Observer works, Mutation Observer’s asynchronousity is useful in this situation.

Challenges

  1. Chrome Extension Architecture The biggest and primary challenge I had making Blissfully (less-than 20% of the code took more than 80% of my time to write) revolved around how Chrome’s data storage works. Chrome extensions rely on a concept called ‘isolated-worlds’, in which each section of a chrome plugin live in their own bubble, as to avoid conflicts and to ensure security. What this means, though, is that accessing localStorage in a content script will yield different results than when accessing it within the chrome extension’s popup page, or the options page. This might seem obvious and make a lot of sense in hindsight, but at the time, with no familiarity of how Chrome extensions are structured and function, sharing data between different parts of Blissfully became a nuisance.

There are APIs for Extensions that allow message sending between different parts of the app, and to sync storage data, but again, when I knew little about each one, and when I rushed into attempting to use them without knowing key facts and details on their functions, it unneccessarily cost me time.

  1. Facebook’s DOM - Quirky with AJAX I learned a ton about Facebook’s DOM through this project. Long story short: everything is loaded with AJAX. When you go from, say, your profile page to the home page, all the content you see is being loaded through AJAX, instead of serving a new page. While this is great for FB because it makes the site snappier, this complicated the plugin because I only wanted the script to run when the user was on the homepage, but due to in-page AJAX requests, the script can’t tell that the user has changed pages because technically they haven’t. I had to come up with a workaround that ensured my script was only running when I wanted it to run.

Lessons Learned

  1. Read the docs before going to Stack Overflow, and take more time upfront trying to understand them. By skipping the docs and going straight to SO for a specific answer, I came across some answers that left out key details of how APIs work that ended up costing me time in the long run. This could have been avoided if I spent a little extra time up front reading through the docs. Jumping in is exciting, but sometimes a bit more theory first can be more effective.

  2. Get an overview of a new platform’s architecture before jumping right in. Had I known a bit more about Chrome extensions being made up of ‘isolated worlds’ that all live separately from each other, I could have avoided some headaches and confusion on why some data was not loading like intended. I don’t like sitting through tutorials and like getting my hands dirty right away, but maybe spending a bit more time with a broad overview upfront or a ‘getting started’ tutorial could have been well-worth it.

If you made it this far, thank you for reading. Feel free to reach me on LinkedIn or on twitter @hagepat!


Patrick El-Hage

I'm Patrick El-Hage and I live and work in San Francisco. I'm also on Twitter.