Bookmarklets (and Custom URL Schemes) Are Criminally Underrated
You Can Add Custom Buttons To Your Browser That Do Whatever
"Bookmarklets" are an underutilized and frankly little-known feature in Chrome and Firefox (and probably most other browsers too) that allows you to create a bookmark that contains a little bit of JavaScript instead of a link to a web page.
That doesn't seem too exciting, but combined with the ability to put bookmarks on a bookmark toolbar, we can create custom buttons that can take arbitrary action within the context of the currently-loaded page, including sending the URL or content from that page to another one, or even to an external program.
This is actually an extremely powerful pattern because it essentially allows a capable user to add custom functionality to the browser that can be used with a click, allowing them to build a totally custom UI.
For instance, on the simple end, I use a bookmarklet to send pages to Kagi's Universal Summarizer. Kagi provided this bit of code in their docs. I've created a bookmark on my Bookmark Toolbar in Firefox and set the Name to "Summarize" and the URL to the following:
javascript:location='https://kagi.com/summarizer/index.html?url='+encodeURIComponent(location)
This bit of JavaScript sends the URL as a query argument to Kagi's AI summarizer product, and redirects the browser there. Pretty cool! This only works for Kagi subscribers, but presumably there are other places where it could be useful to use the location
and send it to an external location when you press a button on your new fancy "custom actions" bookmarklets toolbar.
At work, I take a lot of notes and I frequently want to capture a link to the Jira ticket that I'm working on, so I wrote a small bookmarklet to do exactly that. It extracts the ticket ID and title from the page, and creates an Org link (which could just as easily be Markdown or something else) and places it in the clipboard so I can paste it in my notes.
javascript:(
function() {
var ticketNumber = window.location.pathname.split('/').pop();
var title = document.title;
var orgLink = `TODO [[https://jira.example/browse/${ticketNumber}][${ticketNumber} - ${title}]]`;
navigator.clipboard.writeText(orgLink);
})();
This is all relatively rudimentary but that's because these are quick-and-dirty solutions and I wanted to choose something simple to demonstrate. Presumably one could do something fancier when extracting the title than just grabbing the contents of the <title>
if one needed to, but this works for me, and it shows that inside the bookmarklet, we have access to the DOM that is loaded for the current page. That means we can write JavaScript that will run on whatever arbitrary site data might be loaded, and do whatever we want with it. The sky is the limit!
Bookmarklets have many uses. I've written ones to subscribe to RSS feeds in Miniflux, and ones that would send a URL to ArchiveBox for archiving, and I'm sure more uses will arise.
But buried in the Org documentation I stumbled on a nugget of information that applies more broadly to how computers decide to handle hyperlinks, and how we can define our own to use with bookmarklets to open links outside of the browser: it's possible to launch external applications and send them the current DOM by registering custom URL schemes and opening them with a bookmarklet.
You've Never Seen Hyperlinks Like This Before
Most of the time, the target URL of a link encountered on the web will look like the ones in the examples above, starting with https://
because they are Web links and we expect to get back HyperText (the HT from HTML) from the Hyper Text Transfer Protocol (HTTP). The part of the URL that is usually https
is called the "scheme". Your OS knows that when it sees https
as the scheme, it should send the URL to a Web browser. So it follows that the URL standard allows links to use any number of schemes to launch other applications, as well.
The first example of a hyperlink with a non-https
scheme, you've already seen, because it is used in the two examples above. The reason that this trick works is because the browser accepts javascript:
as URL scheme1 but there many many2 more protocols that have standard schemes, which may or may not do something on your system if you clicked on one in your browser. For instance, an IRC link with a target of the form ircs://irc.libera.chat:6667
like this link to the popular Libera Chat IRC network might open your IRC client if you have one installed because you're a massive nerd. Depending on your browser and settings, it may also just display an error3, for historical reasons related to Internet Relay Chat specifically.
What whis means is that it is possible to open arbitrary applications on your system through a bookmarklet while sending data extracted and preprocessed from the current page by custom JavaScript to that application with the click of a button.
Here's a practical example.
Everything Always Comes Back to Emacs With Me (But It Doesn't Have To For You)
I found out about this functionality through Emacs, because I was trying to discover how I could easily jump from source code in my browser in work's code forge, to the same code in my editor, wherever I have the project checked out locally. Here is an example of registering a custom URL scheme and wiring it up through a bookmarklet to do that.
Hiding in plain sight (in the Org documentation) is a gem4 where functionality for capturing information through a bookmarklet into Emacs is documented. It is extremely unclear from the Org documentation itself, just how cool this little trick is, because the documentation is focused on usage of the feature from the perspective of someone who wants to solve the problem of capturing web data and recording it in Org, in Emacs, and is already familiar with a bunch of Org and Emacs jargon; the feature is called the Org Protocol
.
It's a great example of the power of custom URL schemes and from this example we can learn how to create custom URL schemes that actually work! Well, at least in Linux. I haven't tried the instructions for Windows or Mac, and it seems like at least the ones for Mac might not work anymore, so you're on your own there. Reason forty billion Linux is the best π π€and sorry to disappoint if you're reading this from one of those "Macintosh" computers.
In Linux, most desktop environments these days follow the XDG Specification for determining things like where configuration files go and what applications handle links for particular protocols. XDG defines a format5 for various types of application shortcuts called a "desktop file" and the Org docs define one for creating a new Emacs frame with emacsclient
when a link is followed:
[Desktop Entry]
Name=org-protocol
Comment=Intercept calls from emacsclient to trigger custom actions
Categories=Other;
Keywords=org-protocol;
Icon=emacs
Type=Application
Exec=emacsclient -- %u
Terminal=false
StartupWMClass=Emacs
MimeType=x-scheme-handler/org-protocol;
Most often I have seen .desktop
files used to define for custom application launchers, but this one creates a new scheme handler by creating a new custom MIME sub-type, which will then be registered in the system database.
A MIME type6 represents a format, usually in a type/subtype
pattern like text/html
or audio/mp3
although they can be more complex. They're metadata used to tell software how to process or display a piece of data.
x-scheme-handler
is a MIME type that XDG-compliant desktops recognize as a custom scheme handler, and setting the subtype
to org-protocol
defines a new scheme type.
Exec
tells the desktop what command to run to handle the URLs (%u
), and the rest is metadata.
The docs then tell us to save this file in ~/.local/share/applications
as something like org-protocol.desktop
and run this:
update-desktop-database ~/.local/share/applications/
This updates the system with the new scheme handler. Now that it is registered, org-protocol://
links are valid for the current user on the system, and we can write bookmarklets that use them. This strategy could be used for any application, of course, but in this example I'm using it to open source code in Emacs when I'm viewing it in GitHub or Gitlab, by pressing my magic Open in Emacs
bookmarklet wherein I have set the target URL to this:
javascript:location.href='org-protocol://open-source://'+encodeURIComponent(location.href)
Cool! org-protocol://
gets handled by the browser, and passed to the desktop. The desktop follows the XDG standard, and looks up the scheme handler, finding our .desktop
file and executing the command in Exec
with open-source://full-URL
passed to Emacs. There, Emacs recognizes its "sub-protocol" of open-source://
and there I have defined rules to map repos from their web representations back to where I have them checked out.
This is cool enough as an Emacs feature7, but what is really neat here is that with a .desktop
file and a bookmarklet, you can send Web data directly from the browser to any local application! That's super powerful!
A Warning
Piping data out of your browser can be dangerous! Don't install bookmarklets you find online from untrustworthy sources, as they have access to everything in your browser. Be safe out there.
The Point of This Whole Post β Bookmarklets Are Criminally Underrated
This whole cumbersome process, which has taken me much longer to explain than I anticipated, strings together protocols and specifications designed by optimistic engineers in the early eras of their respective browser projects, who imagined a world where data could flow between applications freely, and users would be able to compose their tools and their data in novel ways to do their work more effectively.
The fact that it is possible to put JavaScript into the URL field of a bookmark is a miracle, something that is surely a quirk that hearkens back to the time before every application ("app" nowadays, I fucking guess) was a walled garden. I suspect it persists because it is invisible to those who don't know about it, and those that do, love the feature. Sure, sure, you could write a whole extension and whatnot, but as we've seen, the simplest bookmarklet is just a JavaScript redirect, and in combination with the custom scheme handler trick Org uses, it's possible to have a hell of a lot of fun.
I just wish it was easier to do all of this stuff. Adding custom buttons to your applications, that do custom things, in combination with other applications on your computer β that should be mundane. It should be everyday. It shouldn't require a bunch of fiddling, and a text file, and a few commands, and a long blog post. But at least it's possible (at least in the best desktop environments) for now.
How do we get to a world where our GUIs are as powerful and extensible as our CLIs? When I click Open in Emacs
on my bookmark toolbar, and Emacs opens to the code I had open in my browser, I feel like I get a little glimpse of that world that could be. Or maybe, as evidenced by Firefox refusing, at least by default, to open IRC links, it's a world that could have been, that we mostly missed out on, visible only with a bit of hackery and inaccessible to most users.
Well, at least for now my bookmarklets still work.
Footnotes
some common URI schemes (note that URI and URL are equivalent)
When I tested this link it worked as I expected in Chromium, opening Hexchat, but Firefox gave me a scary warning. I wasn't expecting Firefox to give me the scary warning, so I hope it's not an indication that Mozilla is about to take away literally all the functionality that I'm excited about in this post. At least, they will probably let me override things in about:config
if worst comes to worst.
Specification for .desktop files β it's really not as big/bad as it looks at first
I should note for anyone reading this blog and deciding to try out the snippets, that while I have gotten "Open in Emacs" working through the method described in this post, I have not gotten everything else described in the linked Org Protocol docs working, for reasons I don't entirely understand. The specific example is a little fiddly in practice and may require some tinkering. Through the Exec
field in the .desktop
file, however, data can be piped to any program for any necessary troubleshooting. Good luck π