Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix user-defined markup links targets (#29305) #29666

Merged
merged 2 commits into from
Mar 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions web_src/js/markup/anchors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {svg} from '../svg.js';

const headingSelector = '.markup h1, .markup h2, .markup h3, .markup h4, .markup h5, .markup h6';

// scroll to anchor while respecting the `user-content` prefix that exists on the target
function scrollToAnchor(hash, initial) {
// abort if the browser has already scrolled to another anchor during page load
if (initial && document.querySelector(':target')) return;
Expand All @@ -19,6 +20,7 @@ function scrollToAnchor(hash, initial) {
export function initMarkupAnchors() {
if (!document.querySelector('.markup')) return;

// create link icons for markup headings, the resulting link href will remove `user-content-`
for (const heading of document.querySelectorAll(headingSelector)) {
const originalId = heading.id.replace(/^user-content-/, '');
const a = document.createElement('a');
Expand All @@ -31,5 +33,18 @@ export function initMarkupAnchors() {
heading.prepend(a);
}

// handle user-defined `name` anchors like `[Link](#link)` linking to `<a name="link"></a>Link`
for (const a of document.querySelectorAll('.markup a[href^="#"]')) {
const href = a.getAttribute('href');
if (!href.startsWith('#user-content-')) continue;
const originalId = href.replace(/^#user-content-/, '');
a.setAttribute('href', `#${encodeURIComponent(originalId)}`);
if (document.getElementsByName(originalId).length !== 1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, getElementsByName?
Isn't that the wrong call?
Shouldn't it be getElementsByID?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know, we use that method because we are considering the cases that we use <a name="link"><\a>. In this condition, we are making it so that those anchors that do not use this notation work as intended by having the event listener.

Copy link
Member

@silverwind silverwind Mar 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's correct. It targets specifically links with name. A likely better and more specific selector could be document.querySelectorAll('.markup a[name]') so we don't accidentially select other things, but lets not do this in a backport.

Copy link
Member

@silverwind silverwind Mar 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even better selector: a.closest('.markup').querySelectorAll('a[name]')

This will ensure it will only search for the target in the current markup document which is important because there can be multiple documents being rendered. I can follow up with this change targeting main branch later.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followup: #29679

a.addEventListener('click', (e) => {
scrollToAnchor(e.currentTarget.getAttribute('href'), false);
});
}
}

scrollToAnchor(window.location.hash, true);
}
Loading