simonw / simonwillisonblog

The source code behind my blog
https://simonwillison.net/
Apache License 2.0
188 stars 17 forks source link

JavaScript permalink headings on homepage too #410

Closed simonw closed 6 months ago

simonw commented 6 months ago

Currently:

CleanShot 2024-03-05 at 09 13 46@2x

But on the article page:

CleanShot 2024-03-05 at 09 13 59@2x

The "Definitions" header only has a permalink added on the article page, not on the homepage.

simonw commented 6 months ago

This code is in entry.html:

https://github.com/simonw/simonwillisonblog/blob/a9eac4502ef52242421641180ba3c871c48c7d5a/templates/entry.html#L9-L24

I want to run it on the homepage too, but it needs to know the URL of the article in order to provide a working link.

simonw commented 6 months ago

Decided to use claude-3-opus via llm chat -m claude-3-opus. Here's the log:

2024-03-05T17:11:53 conversation: 01hr7sh83jha45fav6jhjvgkkv

Model: claude-3-opus-20240229

Prompt:

document.addEventListener('DOMContentLoaded', () => {
  document.querySelectorAll('h4[id],h5[id],h6[id]').forEach(el => {
    id = el.getAttribute('id');
    let hashLink = document.createElement('a');
    hashLink.style.borderBottom = 'none'; 
    hashLink.style.color = '#666';
    hashLink.style.fontSize = '1em';
    hashLink.style.opacity = 0.8;
    hashLink.setAttribute('href', '#' + id);
    hashLink.innerText = '#';
    el.appendChild(document.createTextNode(' '));
    el.appendChild(hashLink);
  });
});

I want a new version of that code which looks for headers that are inside any element with a data-permalink-context="https://example.com/url/here" attribute - headers that are not inside an element like that are left alone, headers inside an element that IS like that use the URL from the attribute in front of the '#'

Response:

Here's the updated version of the code that checks for headers inside elements with the data-permalink-context attribute and uses the URL from the attribute in front of the '#':

document.addEventListener('DOMContentLoaded', () => {
  document.querySelectorAll('h4[id],h5[id],h6[id]').forEach(el => {
    const id = el.getAttribute('id');
    const permalinkContext = el.closest('[data-permalink-context]');

    if (permalinkContext) {
      const url = permalinkContext.getAttribute('data-permalink-context');
      const hashLink = document.createElement('a');
      hashLink.style.borderBottom = 'none';
      hashLink.style.color = '#666';
      hashLink.style.fontSize = '1em';
      hashLink.style.opacity = 0.8;
      hashLink.setAttribute('href', url + '#' + id);
      hashLink.innerText = '#';
      el.appendChild(document.createTextNode(' '));
      el.appendChild(hashLink);
    }
  });
});

In this updated code:

  1. The querySelectorAll selector remains the same, selecting all <h4>, <h5>, and <h6> elements with an id attribute.

  2. Inside the forEach loop, we first retrieve the id of the current header element using el.getAttribute('id').

  3. We then use el.closest('[data-permalink-context]') to find the nearest ancestor element with the data-permalink-context attribute. If such an element is found, it will be assigned to the permalinkContext variable. If no such element is found, permalinkContext will be null.

  4. We check if permalinkContext is truthy (not null or undefined). If it is, it means the header is inside an element with the data-permalink-context attribute.

  5. If the header is inside an element with the data-permalink-context attribute, we retrieve the URL from the attribute using permalinkContext.getAttribute('data-permalink-context') and assign it to the url variable.

  6. We create the hashLink element as before, but this time we set the href attribute to the concatenation of the url and the '#' + id.

  7. The rest of the code remains the same, appending the hashLink to the header element.

With this updated code, headers that are inside an element with the data-permalink-context attribute will have the URL from the attribute prepended to the '#' in the generated hash link. Headers that are not inside such an element will be left alone and will not have any hash link generated.

simonw commented 6 months ago

That worked. Fragment links now show up on: