-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Fix and rewrite markup anchor processing #29931
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
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
681ed05
Fix wiki toc link borken for non-English languages
lunny 3fb120d
Use decodeURIComponent but not encodeURIComponent
lunny ce8a023
rewrite the function to single click listener
silverwind 6689ef6
fix early exit
silverwind cb2e65d
add TODO
silverwind File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,68 @@ | ||
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) { | ||
function scrollToAnchor(encodedId, initial) { | ||
// abort if the browser has already scrolled to another anchor during page load | ||
if (initial && document.querySelector(':target')) return; | ||
if (hash?.length <= 1) return; | ||
const id = decodeURIComponent(hash.substring(1)); | ||
const el = document.getElementById(`user-content-${id}`); | ||
if (el) { | ||
el.scrollIntoView(); | ||
} else if (id.startsWith('user-content-')) { // compat for links with old 'user-content-' prefixed hashes | ||
if (!encodedId || (initial && document.querySelector(':target'))) return; | ||
const id = decodeURIComponent(encodedId); | ||
let el = document.getElementById(`user-content-${id}`); | ||
|
||
// check for matching user-generated `a[name]` | ||
if (!el) { | ||
const nameAnchors = document.getElementsByName(`user-content-${id}`); | ||
if (nameAnchors.length) { | ||
el = nameAnchors[0]; | ||
} | ||
} | ||
|
||
// compat for links with old 'user-content-' prefixed hashes | ||
if (!el && id.startsWith('user-content-')) { | ||
const el = document.getElementById(id); | ||
if (el) el.scrollIntoView(); | ||
} | ||
|
||
if (el) { | ||
el.scrollIntoView(); | ||
} | ||
} | ||
|
||
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'); | ||
a.classList.add('anchor'); | ||
a.setAttribute('href', `#${encodeURIComponent(originalId)}`); | ||
a.innerHTML = svg('octicon-link'); | ||
a.addEventListener('click', (e) => { | ||
scrollToAnchor(e.currentTarget.getAttribute('href'), false); | ||
}); | ||
heading.prepend(a); | ||
} | ||
const markupEls = document.querySelectorAll('.markup'); | ||
if (!markupEls.length) return; | ||
|
||
for (const markupEl of markupEls) { | ||
// create link icons for markup headings, the resulting link href will remove `user-content-` | ||
for (const heading of markupEl.querySelectorAll(`:is(h1, h2, h3, h4, h5, h6`)) { | ||
const originalId = heading.id.replace(/^user-content-/, ''); | ||
const a = document.createElement('a'); | ||
a.classList.add('anchor'); | ||
a.setAttribute('href', `#${encodeURIComponent(originalId)}`); | ||
a.innerHTML = svg('octicon-link'); | ||
heading.prepend(a); | ||
} | ||
|
||
// remove `user-content-` prefix from links so they don't show in url bar when clicked | ||
for (const a of markupEl.querySelectorAll('a[href^="#"]')) { | ||
const href = a.getAttribute('href'); | ||
if (!href.startsWith('#user-content-')) continue; | ||
const originalId = href.replace(/^#user-content-/, ''); | ||
a.setAttribute('href', `#${originalId}`); | ||
} | ||
|
||
// add `user-content-` prefix to user-generated `a[name]` link targets | ||
// TODO: this prefix should be added in backend instead | ||
for (const a of markupEl.querySelectorAll('a[name]')) { | ||
const name = a.getAttribute('name'); | ||
if (!name) continue; | ||
a.setAttribute('name', `user-content-${a.name}`); | ||
} | ||
|
||
// 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 (a.closest('.markup').querySelectorAll(`a[name="${originalId}"]`).length !== 1) { | ||
for (const a of markupEl.querySelectorAll('a[href^="#"]')) { | ||
a.addEventListener('click', (e) => { | ||
scrollToAnchor(e.currentTarget.getAttribute('href'), false); | ||
scrollToAnchor(e.currentTarget.getAttribute('href')?.substring(1), false); | ||
}); | ||
} | ||
} | ||
|
||
scrollToAnchor(window.location.hash, true); | ||
scrollToAnchor(window.location.hash.substring(1), true); | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.