4 min read

Using Vanilla Javascript for Building a Chrome Extension

Using Vanilla Javascript for Building a Chrome Extension

As a developer, I recently took on the challenge of building a Chrome extension to restore the Twitter branding using native JavaScript, without relying on any external libraries. It was an exciting and educational journey, and I'd like to share some of the valuable lessons I learned along the way.

1. Embracing Vanilla JavaScript:
Using only native JavaScript without any libraries or frameworks allowed me to understand the core language features better. It also made the extension lightweight and more efficient, as there were no additional dependencies to load. The best resource to help you with this is MDN Web Docs https://developer.mozilla.org/en-US/docs/Web/API

2. XPath for Dynamic Elements
Modern front end often changes, so selecting elements by XPath proved to be more robust than relying on fixed CSS selectors. By using document.evaluate() function, I could target dynamic elements efficiently, making the extension more resistant to UI changes.

function getElementByXpath(path) {
    return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}

// find the spans with text=Post and replace text to=Tweet
  try {
      do{
          let elem =getElementByXpath("//span[text()='Post']");
          if (elem) { 
              elem.innerHTML = 'Tweet' 
          };
      }while(elem);
  } catch (e) {
      console.log('b2T: Applying Twitter : Error', e);
      delayBack2Twitter();
  }

3. Graceful Error Handling
Since web pages can change unexpectedly, it's essential to have robust error handling. In the extension, I used try-catch blocks to handle potential errors when updating the elements. In case of an error, the extension would retry the update after a short delay, ensuring it would eventually succeed.

// delay the rebranding and attempt after a few ms.
var b2tTimeout = null;
function delayBack2Twitter(){
    //expire after 10 attempts
    if (b2TCounter > 10){
        console.log('b2TCounter exceeding 10 execution');
        return;
    }
    //IMP: remove the existing timeout
    if(b2tTimeout){
        clearTimeout(b2tTimeout);
    }

    b2tTimeout = setTimeout(back2Twitter, 200 /* ms */)
}

//Example: using delaying the function
try {
      //do some action
} catch (e) {
      console.log('b2T: Error', e);
      delayBack2Twitter();
}

4. Handling Asynchronous Page Load
When building a Chrome extension, it's crucial to handle different page loading scenarios. In the code, I checked whether the document was fully loaded, and if not, I added an event listener for the DOMContentLoaded event. This ensured that branding updates were applied correctly, regardless of when the extension was triggered.

// did the page is still loading or completely loaded
if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", back2Twitter);
} else {
    back2Twitter();
}

5. Using Event Listeners Effectively
Twitter is a single-page application, so I needed to detect URL changes to ensure branding restoration whenever the user navigated within the app. By adding a click event listener to the document.body, I could detect URL changes and trigger branding updates accordingly. I utilized event listeners to monitor even page resizing. These event listeners were crucial in triggering the rebranding process at the right times.

// Detect Url Change without a page reload
var b2TUrl = location.href;
document.body.addEventListener('click', () => {
    requestAnimationFrame(() => {
        b2TUrl !== location.href && delayBack2Twitter();
        b2TUrl = location.href;
    });
}, true);

// rebrand when the page size changes
addEventListener("resize",  back2Twitter);

6. Applying SVG Branding
The extension involved replacing the Twitter branding elements (e.g., logo, title) with custom SVGs. I learned how to manipulate the DOM using native JavaScript to update the elements dynamically.

const twitterSvg = '...'; // The Twitter SVG code

try {
    // Update Twitter logo using the custom SVG
    document.querySelector('[href="/home"][aria-label="Twitter"][role="link"] svg').outerHTML = twitterSvg;
} catch (e) {
    console.log('b2T: Applying Logo : Error', e);
    delayBack2Twitter();
}

7. Considering Performance Impact:
As an extension developer, it's essential to consider performance implications carefully. Running content scripts on every page load might impact the browsing experience. I made optimizations, such as limiting the number of rebranding attempts and delaying the process to strike a balance between functionality and performance.



The journey of building this extension has highlighted the significance of robust error handling, dealing with dynamic web pages, and efficiently updating the DOM. I also gained a deeper understanding of how XPath can be a valuable tool for selecting elements in a modern front-end environment (reactjs,vuejs etc).

GitHub - ssv445/back2twitter: Chrome Extension to Go Back To Twitter Branding at Twitter.com
Chrome Extension to Go Back To Twitter Branding at Twitter.com - GitHub - ssv445/back2twitter: Chrome Extension to Go Back To Twitter Branding at Twitter.com

The complete source code for the extension is available in the back2twitter github repo

As developers, we constantly strive to improve our skills and approaches, and I'm eager to learn from others too. If you see any opportunities for improvement or have suggestions for a better implementation of certain functionalities, I'd be thrilled to hear from you.