Using TamperMonkey to Clean Up Websites

I’ve written a few Tampermonkey userscripts to improve websites that I regularly use, and I wanted to share some patterns that I have found useful.

Generally I iterate on the scripts in the Tampermonkey Chrome Extension editor, and then push to GitHub for versioning and backup.

Example

A good example is the script to clean up my preferred weather site (Weather Underground). The script removes ads, as well as removing a sidebar that takes up room but doesn’t add much value.

Before:

Before the Tampermonkey userscript

After:

After the Tampermonkey userscript

Setup

Most of the time in these scripts, I’m finding DOM elements to hide or manipulate them. I could use element selectors, but I typically import jQuery to make this easier and more powerful:

// @require      https://code.jquery.com/jquery-3.6.0.min.js

Apparently $ as an alias for jQuery doesn’t automatically work in Tampermonkey, so add it:

const $ = window.$;

The Tampermonkey template for scripts uses an IIFE (Immediately Invoked Function Expression) to avoid polluting the global namespace. I like to add a use strict directive to avoid some simple JavaScript mistakes and log out that the script is running to make debugging a little easier.

(function() {
  console.log('in wunderground script');
  'use strict';
  ...

Hiding page elements

Almost every script I make has a hideStuff() function. As the name implies, it hides elements on the page. Usually this is going to be for elements that I don’t want or need, or for ads that aren’t blocked by my ad blocker.

function hideStuff() {
  // use whole screen for hourly forecast table
  $('.has-sidebar').removeClass('has-sidebar');
  $('.region-sidebar').hide();

  // hide ads
  $('ad-wx-ws').hide();
  $('ad-wx-mid-300-var').hide();
  $('ad-wx-mid-leader').hide();

  // bottom ad content
  $('lib-video-promo').hide();
  $('lib-cat-six-latest-article').hide();
}

I usually call it in a setInterval. This helps handle cases where the page takes a while to load, or in case elements are loaded asynchronously. This could also work well for single-page apps where the page doesn’t reload.

setInterval(hideStuff, 250);

Sometimes if the page loads quickly I’ll put a couple of setTimeouts with small timeouts at the beginning and then a longer setInterval. It doesn’t really cost much either way, so I usually play around with the timing until it works well.

Keyboard shortcuts

I enjoy using keyboard shortcuts to zip around, but many sites don’t have them. In some more advanced scripts, I’ll add key handlers for custom keyboard shortcuts.

For example, here I’ve added shortcuts for the next and previous day, and to switch between the hourly and 10-day forecasts:

$("body").keypress(function(e) {
  if (e.key === '>' || e.key === '.') {
    $('button[aria-label="Next Day"]').click();
  } else if (e.key === '<' || e.key === ',') {
    $('button[aria-label="Previous Day"]').click();
  } else if (e.key === 'd') {
    $('a span:contains("10-Day")').click();
  } else if (e.key === 'h') {
    $('a span:contains("Hourly")').click();
  }
});

This could break if the page structure changes, but most pages don’t change that often. If they do, I’ll just update the script. Overall I feel like this is pretty easy to read.

My Shortcut.com script has a more involved example of this for adding labels and creating stories, including overriding some existing keybindings. For Feedbin, I implemented a way to scroll stories down half a page (only when the keyboard focus is in the “story” pane).

Conclusion

Overall I think this approach works well to make some of my favorite sites more usable.

It would be great to be able to automatically sync Tampermonkey and the Github repo. Has anyone seen an approach that works well for this?

Categories: main

« Using a Redlock Mutex to Avoid Duplicate Requests Creating A Job Posting Search Engine Using OpenAI Embeddings »

Comments