Skip to content

Section heading anchor copy ("¶") fails silently in modern browsers #308

Description

@h30s

Problem

Inside content pages, headings (h2–h4) have an anchor link () with aria-label="Copy link to this section".

In theme/js/theme.js (lines 153–170), the click listener attempts to copy the URL using setTimeout:

a.addEventListener('click', (e) => {
  // let it navigate, then copy
  setTimeout(() => navigator.clipboard.writeText(window.location.href), 0);
});

Because navigator.clipboard.writeText is called inside a setTimeout callback, it loses the transient user activation (user gesture) context. In modern browsers (Chrome, Safari, Firefox), this throws a NotAllowedError (DOMException: Document is not focused or DOMException: Must be handling a user gesture).

In addition:

  • There is no .catch() error handler or fallback (unlike the code block copy button in the same file, which uses a fallback document.execCommand('copy')).
  • There is no visual feedback (such as a brief "Copied!" indicator) to show the user whether the copy succeeded or failed.

User Impact

When a user clicks the anchor symbol () next to a heading expecting to copy the link to that section, nothing is copied to their clipboard, and the failure occurs silently without any error notification or feedback.

Expected Behavior

  • Clicking the section anchor link should copy the section's URL (including #heading-id) synchronously within the user gesture or with a working fallback.
  • The user should receive visual feedback confirming that the link has been copied.

Relevant Code References

  • theme/js/theme.js (lines 153–170):
    const contentRoot = document.querySelector('main .content-inner') || document.querySelector('main');
    if (contentRoot) {
      contentRoot.querySelectorAll('h2[id], h3[id], h4[id]').forEach(h => {
        if (h.querySelector('a.anchor-link')) return;
        const a = document.createElement('a');
        a.href = `#${h.id}`;
        a.className = 'anchor-link ms-2';
        a.setAttribute('aria-label', 'Copy link to this section');
        a.innerHTML = '¶';
        a.addEventListener('click', (e) => {
          setTimeout(() => navigator.clipboard.writeText(window.location.href), 0);
        });
        h.appendChild(a);
      });
    }

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions