Text Fragment URL Generator

Text Fragment URL Generator

codepen

Have you ever wanted to share a link that takes someone directly to a specific sentence or paragraph on a webpage? That's what text fragment links mdn let us do, and this tool helps you generate one.

When you share a link to a webpage, people usually land at the top and have to scroll to find what you wanted to show them. This can make for a frustrating experience. The traditional solution was using anchor links, something like #section-name at the end. Not every website has it though.

The Solution: Text Fragments

Text fragments let you link to any text on any webpage, even if the page owner didn't prepare for it. When someone clicks your link, their browser automatically scrolls to and highlights the text you specified.

A text fragment URL looks like this:

https://example.com/article#:~:text=the text you want to highlight

The magic happens in that #:~:text= part. Everything after it tells the browser what text to find and highlight.

Advanced Options

The basic version just highlights a single phrase, but you can get more specific. Highlighting a range of text from a starting phrase to an ending phrase:

#:~:text=Once upon a time,happily ever after

Using context clues when the same text appears multiple times on the page. This finds "I was born at Blunderstone" that comes after "Chapter 1-" and before "-A key event", making sure you highlight the right one:

#:~:text=Chapter 1-,I was born at Blunderstone,-A key event 

Building the Tool

The tool needed to handle two different ways people want to use text fragments:

  1. Simple mode: Highlight a single piece of text
  2. Range mode: Highlight from a start point to an end point

Since these modes conflict with each other, I needed the form to be smart about it. If you start typing in the "Main Text" field, you obviously want simple mode, so the range fields get disabled automatically. Vice versa if you use the range fields.

When you click "Generate URL", the tool needs to construct the special text fragment syntax:

generateBtn.addEventListener("click", () => {
  const values = {
    baseUrl: baseUrlInput.value.trim(),
    prefix: prefixInput.value.trim(),
    suffix: suffixInput.value.trim(),
    text: textInput.value.trim(),
    textStart: textStartInput.value.trim(),
    textEnd: textEndInput.value.trim()
  };

  if (!values.baseUrl) return;

  const buildFragment = () => {
    if (!values.text && !values.textStart && !values.textEnd) return "";

    const encode = encodeURIComponent;
    const parts = [];

    if (values.prefix) parts.push(`${encode(values.prefix)}-`);

    if (values.text) {
      parts.push(encode(values.text));
    } else {
      parts.push(encode(values.textStart));
      if (values.textEnd) parts.push(encode(values.textEnd));
    }

    if (values.suffix) parts.push(`-${encode(values.suffix)}`);

    return `text=${parts.join(",")}`;
  };

  const fragment = buildFragment();
  outputUrlSpan.textContent =
    values.baseUrl + (fragment ? `#:~:${fragment}` : "");
  visitBtn.disabled = false;
  visitBtn.classList.remove("sr");
});

Important parts:

  • URL encoding: Special characters like spaces get converted to %20. The encodeURIComponent() function handles this.
  • Comma separation: When using start and end text, they're separated by a comma
  • Hyphens for context: Prefix uses prefix- and suffix uses -suffix with hyphens
  • Empty check: If no text is entered, just return the base URL without modification

In the end we have two buttons, to copy the url and to open in a new tab. An example of the copy code (stolen from somewhere stackoverflow , thanks Dan Stevens!):

copyBtn.addEventListener("click", () => {
  const urlToCopy = outputUrlSpan.textContent;
  if (urlToCopy) {
    const tempTextArea = document.createElement("textarea");
    tempTextArea.value = urlToCopy;
    document.body.appendChild(tempTextArea);
    tempTextArea.select();
    try {
      document.execCommand("copy");
    } catch (err) {
      console.error("Failed to copy text: ", err);
    } finally {
      document.body.removeChild(tempTextArea);
    }
  }
});