Text fragment linking

Alex Chan published a post today that struck me immediately as something I should s̸t̸e̸a̸l̸ adapt for my own use. It’s a bookmarklet that creates a URL linking to the selected text within a web page. The selected text is called a text fragment, and a link to it will typically cause your browser to scroll to the text in question and highlight it. Here’s an example on the MDN page about text fragments.

Chan’s bookmarklet uses this JavaScript:

javascript:
 1:  const selectedText = window.getSelection().toString().trim();
 2:  
 3:  if (!selectedText) {
 4:    alert("You need to select some text!");
 5:    return;
 6:  }
 7:  
 8:  const url = new URL(window.location);
 9:  url.hash = `:~:text=${encodeURIComponent(selectedText)}`;
10:  
11:  alert(url.toString());

It gets the selected text in Line 1, combines it with the URL of the page and the necessary directives in Lines 8 and 9, and displays it in a alert window in Line 11. If no text is selected, the bookmarklet puts up an error message via Lines 3–6.

This is great, but using it as-is while writing a post would force me to select the fragment URL in the alert window, copy it, and then switch to BBEdit (where I do all my writing) and call a script named Clipboard Link, which creates a Markdown reference link from the URL on the clipboard.1 I wanted an automation that didn’t require that many steps.

So I made this Keyboard Maestro macro:

KM Safari text fragment link

As you can see, this macro is meant to be run while BBEdit is the active application and can be called with the ⌃⌥⌘F hotkey. It executes a slightly edited version of Chan’s JavaScript:

javascript:
1:  const selectedText = window.getSelection().toString().trim();
2:  
3:  const url = new URL(window.location);
4:  url.hash = `:~:text=${encodeURIComponent(selectedText)}`;
5:  
6:  return url.toString();

The differences are that there’s no error handling (that’s done elsewhere in the macro) and it puts the text fragment URL into a Keyboard Maestro variable named InstanceFragmentURL instead of displaying an alert.

If InstanceFragmentURL ends with text=, we know that there was no selected text when the macro was invoked. That’s an error, so the Basso sound is played to tell me I made a mistake, and the macro is canceled. Otherwise, the text fragment URL is put on the clipboard and the Clipboard Link script is run by choosing it from the Blogging submenu of BBEdit’s Scripts menu.

You should know that this macro, like Chan’s bookmarklet, creates only the simplest kind of text fragment URL, and it links to the first instance of the text on the page. That might not be the instance you want to link to. For example, if I select this instance of the word “bookmarklet” on Chan’s page,

Screenshot of blog post with selected text

and call the macro, it will make this link, which goes to the word “bookmarklet” in the post’s title.

The MDN page on text fragments explains a set of directives that can be added to the URL to adjust which instance is linked. The prefix- and -suffix directives should be sufficient to uniquely define the fragment. If I need to do so, I’ll add them manually. Like this. I doubt there’s a good way to automate the addition of these directives, so I’m not going to waste my time trying.

I always know I’m going to learn something when Chan’s blog appears in my RSS feed. Today I was able to use what I learned right away.


  1. I thought I’d written a post about Clipboard Link, but apparently not. I guess I’ll have to do that now.