Skip to content

Commit 9c54fef

Browse files
authored
Shorten markdown heading anchors links (#11903)
This changes the links on headings like '# Usage' in markdown from `https://host/user/repo#user-content-usage` to just `https://host/user/repo#usage` matching GitHub and GitLab. The linked id elements still have the prefix and this behaviour matches GitHub and GitLab too, so JS is needed to scroll to the active anchor. I suspect it's like that to avoid namespace collission between user-generated content and other page content. Compatibilty for old links is included so they will continue to work. Also included are some enhancements to make the clickable area for the link icon larger and fix its color on arc-green. Fixes: #11896 Fixes: #12062
1 parent 4b66d9a commit 9c54fef

File tree

3 files changed

+67
-73
lines changed

3 files changed

+67
-73
lines changed

web_src/js/index.js

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import './publicpath.js';
66
import Vue from 'vue';
77
import 'jquery.are-you-sure';
88
import './vendor/semanticdropdown.js';
9-
import {svg} from './utils.js';
109

1110
import initContextPopups from './features/contextpopup.js';
1211
import initGitGraph from './features/gitgraph.js';
1312
import initClipboard from './features/clipboard.js';
1413
import initUserHeatmap from './features/userheatmap.js';
1514
import initServiceWorker from './features/serviceworker.js';
15+
import initMarkdownAnchors from './markdown/anchors.js';
1616
import attachTribute from './features/tribute.js';
1717
import createDropzone from './features/dropzone.js';
1818
import initTableSort from './features/tablesort.js';
@@ -2360,15 +2360,6 @@ $(document).ready(async () => {
23602360
});
23612361
});
23622362

2363-
// Set anchor.
2364-
$('.markdown').each(function () {
2365-
$(this).find('h1, h2, h3, h4, h5, h6').each(function () {
2366-
let node = $(this);
2367-
node = node.wrap('<div class="anchor-wrap"></div>');
2368-
node.append(`<a class="anchor" href="#${encodeURIComponent(node.attr('id'))}">${svg('octicon-link', 16)}</a>`);
2369-
});
2370-
});
2371-
23722363
$('.issue-checkbox').on('click', () => {
23732364
const numChecked = $('.issue-checkbox').children('input:checked').length;
23742365
if (numChecked > 0) {
@@ -2426,6 +2417,7 @@ $(document).ready(async () => {
24262417
searchTeams();
24272418
searchRepositories();
24282419

2420+
initMarkdownAnchors();
24292421
initCommentForm();
24302422
initInstall();
24312423
initRepository();

web_src/js/markdown/anchors.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {svg} from '../utils.js';
2+
3+
const headingSelector = '.markdown h1, .markdown h2, .markdown h3, .markdown h4, .markdown h5, .markdown h6';
4+
5+
function scrollToAnchor() {
6+
if (document.querySelector(':target')) return;
7+
if (!window.location.hash || window.location.hash.length <= 1) return;
8+
const id = window.location.hash.substring(1);
9+
const el = document.getElementById(`user-content-${id}`);
10+
if (el) {
11+
el.scrollIntoView();
12+
} else if (id.startsWith('user-content-')) { // compat for links with old 'user-content-' prefixed hashes
13+
const el = document.getElementById(id);
14+
if (el) el.scrollIntoView();
15+
}
16+
}
17+
18+
export default function initMarkdownAnchors() {
19+
if (!document.querySelector('.markdown')) return;
20+
21+
for (const heading of document.querySelectorAll(headingSelector)) {
22+
const originalId = heading.id.replace(/^user-content-/, '');
23+
const a = document.createElement('a');
24+
a.classList.add('anchor');
25+
a.setAttribute('href', `#${encodeURIComponent(originalId)}`);
26+
a.innerHTML = svg('octicon-link', 16);
27+
heading.prepend(a);
28+
}
29+
30+
scrollToAnchor();
31+
window.addEventListener('hashchange', scrollToAnchor);
32+
}

web_src/less/_markdown.less

Lines changed: 33 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,51 @@
3030
}
3131

3232
.anchor {
33-
position: absolute;
34-
top: 0;
35-
left: 0;
36-
display: block;
37-
padding-right: 6px;
38-
padding-left: 30px;
39-
margin-left: -30px;
33+
padding-right: 4px;
34+
margin-left: -20px;
35+
line-height: 1;
36+
color: inherit;
37+
}
38+
39+
.anchor .svg {
40+
vertical-align: middle;
4041
}
4142

4243
.anchor:focus {
4344
outline: none;
4445
}
4546

47+
h1 .anchor .svg,
48+
h2 .anchor .svg,
49+
h3 .anchor .svg,
50+
h4 .anchor .svg,
51+
h5 .anchor .svg,
52+
h6 .anchor .svg {
53+
visibility: hidden;
54+
}
55+
56+
h1:hover .anchor .svg,
57+
h2:hover .anchor .svg,
58+
h3:hover .anchor .svg,
59+
h4:hover .anchor .svg,
60+
h5:hover .anchor .svg,
61+
h6:hover .anchor .svg {
62+
visibility: visible;
63+
}
64+
65+
h2 .anchor .svg,
66+
h3 .anchor .svg,
67+
h4 .anchor .svg {
68+
position: relative;
69+
top: -2px;
70+
}
71+
4672
h1,
4773
h2,
4874
h3,
4975
h4,
5076
h5,
5177
h6 {
52-
position: relative;
5378
margin-top: 1em;
5479
margin-bottom: 16px;
5580
font-weight: bold;
@@ -60,37 +85,6 @@
6085
}
6186
}
6287

63-
h1 .octicon-link,
64-
h2 .octicon-link,
65-
h3 .octicon-link,
66-
h4 .octicon-link,
67-
h5 .octicon-link,
68-
h6 .octicon-link {
69-
display: none;
70-
color: #000000;
71-
vertical-align: middle;
72-
}
73-
74-
h1:hover .anchor,
75-
h2:hover .anchor,
76-
h3:hover .anchor,
77-
h4:hover .anchor,
78-
h5:hover .anchor,
79-
h6:hover .anchor {
80-
padding-left: 8px;
81-
margin-left: -30px;
82-
text-decoration: none;
83-
}
84-
85-
h1:hover .anchor .octicon-link,
86-
h2:hover .anchor .octicon-link,
87-
h3:hover .anchor .octicon-link,
88-
h4:hover .anchor .octicon-link,
89-
h5:hover .anchor .octicon-link,
90-
h6:hover .anchor .octicon-link {
91-
display: inline-block;
92-
}
93-
9488
h1 tt,
9589
h1 code,
9690
h2 tt,
@@ -113,55 +107,31 @@
113107
border-bottom: 1px solid #eeeeee;
114108
}
115109

116-
h1 .anchor {
117-
line-height: 1;
118-
}
119-
120110
h2 {
121111
padding-bottom: .3em;
122112
font-size: 1.75em;
123113
line-height: 1.225;
124114
border-bottom: 1px solid #eeeeee;
125115
}
126116

127-
h2 .anchor {
128-
line-height: 1;
129-
}
130-
131117
h3 {
132118
font-size: 1.5em;
133119
line-height: 1.43;
134120
}
135121

136-
h3 .anchor {
137-
line-height: 1.2;
138-
}
139-
140122
h4 {
141123
font-size: 1.25em;
142124
}
143125

144-
h4 .anchor {
145-
line-height: 1.2;
146-
}
147-
148126
h5 {
149127
font-size: 1em;
150128
}
151129

152-
h5 .anchor {
153-
line-height: 1.1;
154-
}
155-
156130
h6 {
157131
font-size: 1em;
158132
color: #777777;
159133
}
160134

161-
h6 .anchor {
162-
line-height: 1.1;
163-
}
164-
165135
p,
166136
blockquote,
167137
ul,

0 commit comments

Comments
 (0)