Working with smaller chunks of thoughts; adding anchors to paragraphs in Org Mode HTML export
Posted: - Modified: | org, js- : Fleshed out how to link to text fragments
- elisp code to link to currently-selected text using Spookfox : Added link to
I write my blog posts in Org Mode and export them to Eleventy with ox-11ty, which is derived from the ox-html backend.
Sometimes I want to link to something in a different blog post. This lets me build on thoughts that are part of a post instead of being a whole post on their own.
If I haven't added an anchor to the blog post yet,
I can add one so that I can link to that section.
For really old posts where I don't have an Org
source file, I can edit the HTML file directly and
add an id="some-id"
so that I can link to it
with /url/to/post#some-id
. Most of my new posts
have Org source, though. I have a
my-blog-edit-org
function and a
my-blog-edit-html
function in my Emacs
configuration to make it easier to jump to the Org
file or HTML for a blog post.
If the section has a heading, then it's easy to
make that linkable with a custom name. I can use
org-set-property
to set the CUSTOM_ID
property
to the anchor name. For example, this voice access
section has a heading that has CUSTOM_ID
, as you
can see in the . If I don't mind having
long anchor names, I can use the
my-assign-custom-ids
function from my config to
automatically set them based on the outline path.
my-assign-custom-ids
(defun my-assign-custom-ids () (interactive) (let ((custom-ids (org-map-entries (lambda () (org-entry-get (point) "CUSTOM_ID")) "CUSTOM_ID={.}"))) (org-map-entries (lambda () (let ((slug (replace-regexp-in-string "^-\\|-$" "" (replace-regexp-in-string "[^A-Za-z0-9]+" "-" (downcase (string-join (org-get-outline-path t) " ")))))) (while (member slug custom-ids) (setq slug (read-string "Manually set custom ID: "))) (org-entry-put (point) "CUSTOM_ID" slug))) "-CUSTOM_ID={.}")))
Adding anchors to paragraphs
If the part that I want to link to is not a
heading, I can add an ID by using the
#+ATTR_HTML: :id ...
directive, like this snippet from my reflection on landscapes and art:
That reminds me a little of another reflection
I've been noodling around on interest development...
Anchor links
It might be fun to have a little margin note with 🔗 to indicate that that's a specially-linkable section, which could be handy when I want to link when mobile. It feels like that would be a left margin thing on a large screen, so it'll just have to squeeze in there with the sticky table of contents. I've been meaning to add link icons to sub-headings with IDs, anyway, so I can probably solve both with a bit of Javascript.
/* Add link icons to headings and anchored paragraphs */ function addLinkIcons() { document.querySelectorAll('h1[id], h2[id], h3[id], p[id]').forEach((o) => { const link = document.createElement('a'); const article = o.closest('article[data-url]'); link.href = window.location.origin + (article?.getAttribute('data-url') || window.location.pathname) + '#' + o.getAttribute('id'); link.innerHTML = '🔗'; // link icon link.title = 'anchor'; link.classList.add('anchor-icon'); link.addEventListener('click', copyLink); o.prepend(link); }); } addLinkIcons();
And some CSS:
.entry h2, .entry h3, .entry h4, .entry p, .content h2, .content h3, .content h4, .content p { position: relative } .content a.anchor-icon:link, .entry a.anchor-icon:link { font-size: 60%; text-decoration: none !important; font-size: small; float: right } @media only screen and (min-width: 95rem) { .content a.anchor-icon:link, .entry a.anchor-icon:link { display: block; position: absolute; left: -2em; top: 0.2em; float: none} }
Text fragments
Text fragments are even more powerful, because I
can link to a specific part of a paragraph. I can
link to one segment with something like
#:
:text=text+to+highlight~. I can specify
multiple text fragments to highlight by using
#:
:text=first+text+to+highlight&text=second+text~,
and the browser will automatically scroll to the
first highlighted section. I can specify a longer
section by using text=textStart,textEnd. Example:
#:~:text=That%20is%20the%20gap,described The text
fragments documentation has more options,
including using prefixes and suffixes to
disambiguate matches.
Text fragment links require rel="noopener"
for
security, so I added
JKC-Codes/eleventy-plugin-automatic-noopener to my
11ty config.
Update 2025-03-20: Quick ways to link to a text fragment:
- On my Android phone, selecting text in Google Chrome and sharing it automatically includes the text and a link to the text fragment.
- In Google Chrome on my iPad, my process is:
- Select the text and choose "Copy Link with Highlight".
- Tap the selected text again and share it.
- Paste the link after the shared text.
- There's this Text Fragment extension for Firefox.
- I have some Emacs Lisp to link to currently-selected text using Spookfox. Spookfox connects Emacs to Firefox using a browser extension. Once it's properly set up and connected, it allows Emacs to evaluate things in the Firefox context.
These seem like good starting points for addressing smaller chunks of thoughts.