Transform Your dev.to Experience with a Custom Extension!
Tags: webdev, javascript, tutorial, beginners
Published: 2022-12-19 18:19:52

## Preface

Hey there dev.to community!

Have you ever found yourself scrolling through a long article on dev.to, trying to find a specific section? Well, worry no more - today we're going to create a Table of Contents (ToC) extension to make navigation a breeze!

## Steps

- Creating the Extension Structure
- Setting up the Extension for Development
- Building the ToC (Table of Contents)
- Publishing the Extension

### Creating the Extension Structure

```
.
└── dev.toc/
    ├── extension.css
    ├── extension.js
    └── manifest.json
```

### Setting up the Extension for Development

- Chrome & Edge
    1. Click on `⋮` (or `⋯` in Edge) in the top-right corner
    2. Select "More Tools" and "Extensions"
    3. Click on the "Load Unpacked" button on the top-left side of the page.
    4. Choose the folder containing the extension or select the extension manifest file
- Firefox
    1. Go to `about:debugging#/runtime/this-firefox`
    2. Select "Load Temporary Add-ons"
    3. Choose the folder containing the extension or select the extension manifest file

### Building the ToC
First, we’ll add the following to our `manifest.json` file,

```json
{
    "manifest_version": 3, // 2 for firefox
    "name": "Dev.to Table of Contents",
    "short_name": "dev.toc",
    "version": "1.0",
    "description": "Add table of contents to dev.to articles",
    "content_scripts": [
        {
            "matches": [ "https://dev.to/*/*" ],
            "js": ["extension.js"]
        }
    ]
}
```

We specified the following in the manifest file

- manifest_version and extension version
- name and short_name
- description
- content_scripts
    - matches - the URL we need this extension to run
    - js - the `extension.js` we created that’ll add the ToC for the articles

(more about manifest file [here](https://developer.chrome.com/docs/extensions/mv3/manifest/))

We will now work on the `extension.js` file, using the mockup as a reference, to create the Table of Contents

![ToC](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h50wzpffgk14hdiaqrvk.png)

Let’s select the `article-body` element, which is the main container element for the article's content

Next, we create an array of all the heading elements within the `article-body` element by using the `Array.from` method to convert the `articleBody.children` NodeList into an array, and then filtering this array to only include heading tags with the regular expression `/h[1-6]/g`. This regular expression will match any heading element, from `h1` to `h6`.

```jsx
const articleBody = document.getElementById("article-body");
const articleHeadings = Array.from(articleBody.children).filter(
    (e) => /h[1-6]/g.test(e.localName)
);
```

Then we’ll create a `details` element, which will serve as the container for the TOC with a `summary` element and a `ul` element, which will be used as the heading and list, respectively, for the ToC.

```jsx
const tocDetails = document.createElement("details");
const tocSummary = document.createElement("summary");
tocSummary.textContent = "Table of Contents";
const tocList = document.createElement("ul");
```

The code then creates a `li` element and an `a` element, which will be used to create a link to the main title of the article (or the Go to Top link). The `a` element's `href` attribute is set to `#main-title`, and its `textContent` is set to the article title

Next, let’s add an event listener to the `a` element that will prevent the default behavior of the click event (i.e., following the link) and instead scroll the page smoothly back to the top when the link is clicked.

The `li` and `a` elements are then appended to the ToC list, and the ToC list is appended to the `details` element.

```jsx
const titleItem = document.createElement("li");
const titleAnchor = document.createElement("a");
titleAnchor.href = "#main-title";
titleAnchor.textContent = document
    .querySelector(".crayons-article__header__meta h1")
    .textContent.trim();

titleAnchor.addEventListener("click", (e) => {
    e.preventDefault();
    tocDetails.open = false;
    window.scrollTo({ top: 0, behavior: "smooth" });
});

titleItem.append(titleAnchor);
tocList.append(titleItem);

tocDetails.append(tocSummary);
tocDetails.append(tocList);
```

Finally, if there are any heading elements within the `article-body` element, we will create a nested `ul` element and iterate over the heading elements, creating a new `li` and `a` element for each one.

The `a` element's `href` attribute is set to the heading element, and its `textContent` is set to the heading element's text content. An event listener is added to the `a` element that’ll prevent the default behavior and do the smooth scroll to the position of the heading element just like before. The `li` and `a` elements are then appended to the nested `ul` element, and the nested `ul` element is appended to the main ToC list.

```jsx
if (articleHeadings.length > 0) {
    const headingList = document.createElement("ul");
    for (const heading of articleHeadings) {
        const headingItem = document.createElement("li");
        const headingLink = document.createElement("a");
        headingLink.href = Array.from(heading.children)[0].href;
        headingLink.textContent = heading.textContent.trim();

        headingLink.addEventListener("click", (e) => {
            e.preventDefault();
            tocDetails.open = false;
            const headingTop = heading.offsetTop;
            window.scrollTo({ top: headingTop - 35, behavior: "smooth" });
        });

        headingItem.append(headingLink);
        headingList.append(headingItem);
    }
    tocList.append(headingList);
}
```

At last, the ToC is inserted at the beginning of the `article-body` element.

```jsx
articleBody.prepend(tocDetails);
```
**Note**: Be sure to reload the extension after every change you make (The reload button will be on the same page where you load the extension for development purpose)

After adding styling to the elements, the final result will look like this,
- ToC close
![ToC close](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nu2dorr60s2t01mu4t0b.png)
- ToC open
![ToC open](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b9vnbp4forlueij2v693.png)

(I tried my best to match the stylings with dev.to styles)

### Publishing the Extension

- Chrome

Publishing an extension on Chrome is simple. All you need to do is create a developer account, upload your extension through the Chrome Web Store Developer Dashboard, and wait for it to be reviewed by the Chrome team. Once your extension has been approved, it will be available for download to millions of Chrome users around the world.

- Firefox

The process for publishing on Firefox is similar to Chrome, with a developer account and submission through the Firefox Add-ons Developer Hub.

## Next steps

Great job on building a browser extension! Now it's time to take it to the next level and create your own (or improve this extension). Make sure to test it out before publishing. And don't forget to keep it updated, maintain it regularly and share it with the community.

### Resources
{% github DhanushAdithya/dev.toc %}
- [Browser Extensions - MDN Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions)
- [Extensions - Chrome Developers](https://developer.chrome.com/docs/extensions/)

Happy Coding!