Creating my first Chrome Extension
Tags: javascript
Published: 2020-11-03 02:31:32

Last week, after doing some research and learning a bit about how Chrome extensions work, I posted a write up on what the Manifest File is and how to use it. This week, I expanded on that and created my very first (very simple) Chrome extension. This post will go through the process of creating this extension, and talk about what I wanted to create and how I went about doing so. Since I already did a write up on the Manifest File, I will not be going too in depth on what that is. Feel free to check out my post from last week [here](https://dev.to/kjdowns/chrome-extensions-manifest-file-47he) if you want to learn more about that specifically. 

##Why and What Did I Create?##

I had what I felt like was a great interview last week, but it seemed like after a week without an update that I was most likely being ghosted. I decided that I would reach out to the people that I interviewed with for an update. I didn't want to just ask for an update though, I also wanted to show them how dedicated I was and that hiring me should be an obvious decision. I took some time to think what I could create that would leave an impression but also be done relatively quickly.

The company was a relatively large marketing company, so I decided on two features that could possibly be split into two extensions and expanded on in the future. One was for the consumer side that changed the plain text names of their brands into hyperlinks that would take you to the website for that brand. The other feature was imagined more for employees or account managers and consisted of a popup quick menu that provided one click access to client sites. I imagined this could be expanded into a quick access portal for a client information hub.

##Manifest File##

Let's get into the code! The first and most important thing you need to worry about when creating an extension is the Manifest File. This contains all of the information that Chrome needs to make sure all the parts of your extension work together. This is a file that every extension, at minimum, must have. 

You can see what my Manifest looks like in its totality below. I am presenting it all at once in its final iteration for simplicity, but when creating your own you may find yourself adding to or removing from this file as you are working. I have removed company specific information as it's not particularly relevant.

```javascript
{
    "name": "Chrome Extension",
    "version": "1.0",
    "description": "Demo extension that linkifies brands on the web and provides a quick bookmark to brand sites.",
    "manifest_version": 2,
    "browser_action": {
        "default_popup": "popup.html"
    },
    "content_scripts": [
        {
            "matches": ["<all_urls>"],
            "js": ["content.js"]
        }
    ]
  }
```

The first four fields are required. They provide a name and description of the extension, a version for facilitating updates, and the version of the manifest file we are using. (As of now this should almost always be 2)

Since I wanted this extension to work across the entire web, I chose to use the `browser_action` field, inside which I specify which file to be used for the popup feature. Since this was just a prototype project, I opted not to include `icon` information. The default letter icon worked for me, but you may want to think about including that information in your own project.

For the web page functionality portion, I used the `content_scripts` field to specify which JS file to uses when pages load. Inside `content_scripts`, the `matches` field specifies which pages you want your JS to run on. In this case I wanted it to run on all urls. The documentation has great resources on how to include and exclude specific sites if you want to do something different. Lastly, the `js` tag just tells the file where to find my JS file.

##Popup Menu##

As it was the more simple feature to implement, let's talk about the Popup Menu. My idea for this feature was a quick access popup menu with clickable buttons. The buttons would provide one click access to whatever information was needed. For this demo, I chose individual brand websites. 

Creating a popup menu for your extension is as simple as creating an `html` file. Provide the `html` file that you want to the Manifest using the `default_popup` field and Chrome will automatically display your html file as a popup when the extension's tray icon is clicked. You are also able to link CSS and JS files in the HTML file head like a regular HTML file.

As I wanted to keep things simple, I decided to link a single CSS file to handle styling. The contents were a simple heading, brief description, and then a list of icons wrapped in hyperlink tags. I've provided a snippet below of what my code looks like with just a few links.

```html
<body>
    <h3>Linker Extension</h3>
    <p><em>Quick links to Brand websites!</em></p>
    <div class="links-wrapper">
        <ul class="links-list">
            <li class="links-item">
                <div class="link-container">
                    <a href="<LINK_HERE>" target="blank">
                        <img src="<ICON_FILE>">
                    </a>
                </div>
            </li>
            <li class="links-item">
                <div class="link-container">
                    <a href="<LINK_HERE>" target="blank">
                        <img src="<ICON_FILE>">
                    </a>
                </div>
            </li>
        </ul>
    </div>
</body>
```

##Text Replacement##

The second feature, and the one that took me the longest time to figure out how to implement, was replacing plain text instances of brand names on web pages with a link to that specific brand's website. This feature went through a few iterations until I found the way that worked for me, but let's talk a bit about what I wanted to happen and how I decided to do it.

When you visit a page with an extension enabled, after loading the DOM, Chrome then runs whatever JS file is responsible for the extension's behavior. In my case, I needed to parse the text within the DOM for instances of the brands I was working with, generate a hyperlink with the matching text and target site, and then replace that bit of text in the DOM with my new hyperlink.

Since I only had a handful of brands to worry about, I decided to create an array of objects - one for each brand. They would have a name and url key that I could use to easily find and use the information that I needed. The final result looked something like this:

```javascript
const brands = [
    {name: "<BRAND_NAME_1>", url: "<BRAND_URL_1>"},
    {name: "<BRAND_NAME_2>", url: "<BRAND_URL_2>"}
]
``` 

Next, I needed to figure out how to grab all of the elements on the page that had text in them. I wanted to keep it simple and not really have to worry about breaking things on the page, so I decided to only grab `p` and `span` tags as they were the most likely to contain the text I wanted to grab without worrying about breaking existing styling or navigating nested elements.

```javascript
let nodes = document.querySelectorAll('p,span')
```

The script contains one reusable function which I used to generate the link tag that we will be inserting into the DOM. Since I already had all of the brand information I needed as objects, the function accepts a brand object as an argument and returns a string that will serve as our hyperlink element.

```javascript
function generateLinkElement(brandObject){
    return `<a href="${brandObject.url}" target="blank">${brandObject.name}</a>`
}
```

Finally, I needed to implement the actual search and replace operation that will be the basic functionality. It is a simple loop through all of the elements that I grabbed from the DOM. For each one, I then loop through each brand object in my `brands` array. If the brand name is included, it replaces the text with the link element generated by our `generateLinkElement()` function. If not, it continues down the chain until all brands have been checked against each element. 

```javascript
nodes.forEach(node => {
    brands.forEach(brand => {
        if (node.innerHTML.includes(`${brand.name}`)) {
            node.innerHTML = node.innerHTML.replaceAll(`${brand.name}`, `${generateLinkElement(brand)}`);
        }
    });
});
```

##Conclusion##

While this is certainly a simple project, and there are probably more optimal/ better ways to implement it, I definitely learned a lot. It was quite fun to challenge myself to learn something new in a short time frame. If you are interested in creating your own Chrome extension or learning more about how to do it, I highly recommend checking out the official [docs](https://developer.chrome.com/extensions). They are really quite thorough and do a great job of breaking down what you need.