Friday, June 18, 2021

JavaScript Bookmarklets with Greasemonkey

Everyone knows what a bookmark is. It’s just a link to a Web resource that usually begins with the “http:”, “file:”, or “ftp:” protocol. There is another special prefix that browsers recognize as well: “javascript:”. It tells the browser to execute everything that comes after as JavaScript code. Bookmarks that employ the “javascript:” prefix are known as bookmarklets. These “one-click” wonders add functionality to the browser, allowing us to modify the appearance of any web page, querying a search engine with search terms provided by previously selected text, or submitting the current page to a translation or blogging service.

While powerful, bookmarklets are not without limitations. One of the biggest in my view is that you can’t fully automate them. You always have to kick things off by clicking the bookmark link. Sounds like a minor annoyance, but there may be times that you’d like your script to run on the page load event. In fact, it was that very requirement that led me to a browser extension called Greasemonkey. It enables users to install scripts that make on-the-fly changes to web page content after or before the page is loaded in the browser. Since changes made to the web pages are executed every time the page is viewed, Greasemonkey scripts are effectively permanent for the user running the script.

Like Bookmarklets, Greasemonkey can be used for customizing page appearance, adding new functions to web pages (for example, embedding price comparisons within shopping sites), fixing rendering bugs, combining data from multiple web pages, and countless other purposes.

This tutorial will explain how to get started with Greasemonkey by creating a simple script that shows a date & time in a different time zone. Specifically, we will convert the local Eastern Daylight Time (EDT) into Mountain Daylight Time (MDT) using the amazing Luxon Date Library.

Canada and USA Savings Time JavaScript

 

How to Install Greasemonkey

Greasemonkey is a Firefox extension. As such, it’s installed in the same way as any other Firefox extensions:

  1. Select Tools > Add-ons and Themes from the main menu to open the Addons page:
    Installing Greasemonkey Firefx Addon
  2. Type “greasemonkey” in the Find more add-ons input fields, and click the magnifying glass icon, or hit the ENTER key.
  3. Greasemonkey should appear at the top of the search results:Greasemonkey Addon Search Results
  4. Click on the Greasemonkey result to open the Install page.
  5. From the Greasemonkey page, click the blue “+ Add to Firefox” button to install the extension. Firefox will show a warning and ask you to confirm adding the extension. Click “Add”:
  6. Installing Greasemonkey
  7. Once it has finished, you should see Greasemonkey’s monkey icon at the top of the browser window, near the address bar:The Greasemonkey Shortcut

To create a new script, click on the Greasemonkey icon and select New user script… from the menu:

Adding a new script in Greasemonkey

 

The Metadata Block

The metadata block is a section of a user script that describes the script. In addition to typical metadata such as the script name, namespace, and description, it can also include and exclude rules. The metadata block may appear anywhere in the top level Greasemonkey code scope of the script, but is usually at the top of the file. Greasemonkey supports lots of keys, so we’ll concentrate on what we need in our script:

// ==UserScript==
// @name     Greasemonkey Time Zone Demo
// @version  1
// @include https://www.worldtimeserver.com/current_time_in_CA-ON.aspx?city=Kanata
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/luxon/1.26.0/luxon.min.js
// ==/UserScript==

Of particular importance to us are the @include, and @require items.

The @include is a URL filter for when to run the script. It supports wildcards, but to attach the script to a specific page, the more exact the URL, the better. Here is the current date and time for my local time zone:

Adding Current Time with Greasemonkey

 

The @require metadatum is a ridiculously easy way to include external scripts. Each @require resource is downloaded once, when the script is installed, and stored on the user’s hard drive alongside the script. The URL specified may be relative to the URL the script is being installed from. There can be any number of @require keys in a script as well. You’ll see each script in its own tab:

Metadata Block in JavaScript

 

Note that the Query noConflict mode is enabled so that our local script doesn’t interfere with the web page’s own scripts.

Manipulating the DOM

I included jQuery specifically for its superlative DOM methods. Here’s some code that creates a new DIV element and appends the paragraph that will contain the time in MDT:

var timeDiv = $('<div><h4>Time in Calgary</h4></div>')
    .css('border', 'solid red 1px')
    .insertAfter($('.local-time p').last());
var timeP = $('<p></p>').appendTo(timeDiv);

We can omit the document ready handler because, by default, Greasemonkey runs after the main page is loaded, but before other resources (images, style sheets, etc.) have loaded.

To obtain the ‘.local-time p’ selector string, I simply right-clicked the element and selected Inspect (Q) from the context menu:

jQuery Last Element Greasemonkey

 

If I couldn’t figure it out that way, I could also copy the CSS selector using the built-in browser utility:

CSS- Selector Greasemonkey

 

Showing the Converted Time

The challenge here is not showing the converted time, but rather, in synchronizing it with the one in the web page. It’s just a plain ole SPAN tag whose innerText is refreshed every second. SPANs don’t emit an onchange event, so we need to find another way to monitor it. I settled on the DOMSubtreeModified event. Although it’s been deprecated in favor of Mutation Observers, the later requires more code, so I stuck with the easier approach:

$("#theTime").on('DOMSubtreeModified',function(){
  var now = luxon.DateTime.fromObject({zone: 'America/Boise'});
  timeP.text(now.toFormat("FFF"));
});

Calgary is in Mountain Daylight Time, but Luxon takes its time zone parameters in the form of “America/New York”. There is no “America/Calgary”, but I was able to find a time zone with the equivalent UTC Offset in the Wikipedia List of tz database time zones.

For the date & time format, I chose one of Luxon’s many offerings that included all of the information I was looking for, but with a reasonably compact output. Here is a screenshot of the final result:

Luxon Date and Time Script

 

Unfortunately, it does not properly convey the time synchronization. To bask in the script’s full glory, I would urge you to insert the following code into your own Greasemonkey script, and then navigate to worldtimeserver.com to try it out:

// ==UserScript==
// @name     Greasemonkey Time Zone Demo
// @version  1
// @include https://www.worldtimeserver.com/current_time_in_*
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/luxon/1.26.0/luxon.min.js
// ==/UserScript==

//Avoid conflicts
this.$ = this.jQuery = jQuery.noConflict(true);

var timeDiv = $('<div><h4>Time in Calgary</h4></div>')
    .css('border', 'solid red 1px')
    .insertAfter($('.local-time p').last());
var timeP = $('<p></p>').appendTo(timeDiv);
$("#theTime").on('DOMSubtreeModified',function(){
  var now = luxon.DateTime.fromObject({zone: 'America/Boise'});
  timeP.text(now.toFormat("FFF"));
});

I included a wildcard in the @include URL so that you can try it with your own location, but you’ll have to modify the script if you’d like to see a different target time zone.

Conclusion

In this tutorial, we got a taste of Greasemonkey’s tremendous capabilities by creating a simple script that shows a date & time in a different time zone. For someone who is enamored with Bookmarklets like myself, Greasemonkey is a gift that keeps on giving. It’s pretty powerful stuff when you consider that our script is less than ten lines long, not including the metadata block.

Robert Gravelle
Robert Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Popular Articles

Featured