Skip to content

Commit 90e5a31

Browse files
chore: ♻️ remove jquery (#1964)
* add scroll-to-top and scroll class toggle with vanilla JS * add language detection * add i18n * Fix bug: there was no class hidden in CSS. Notice was hidden by hide(). Now HTML hidden attribute is used to hide notice using CSS. This improves A11y. * fix flashing of i18n notice and improve a11y * add menu highlighting on scroll * ♻️ remove jquery * defer app.js Co-authored-by: Sebastian Beltran <[email protected]> * remove DOMContentLoaded and fix root margin to 25% --------- Co-authored-by: bjohansebas <[email protected]>
1 parent 4c0838e commit 90e5a31

File tree

6 files changed

+120
-116
lines changed

6 files changed

+120
-116
lines changed

_includes/head.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@
6464
{% else %}
6565
<meta property="twitter:image" content="https://expressjs.com/images/og.png">
6666
{% endif %}
67-
<script data-cfasync="false" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
68-
<script data-cfasync="false" src="/js/app.js"></script>
67+
<script data-cfasync="false" defer src="/js/app.js"></script>
6968
<script data-cfasync="false" defer src="/js/menu.js"></script>
7069
<script data-cfasync="false" defer src="/js/copycode.js"></script>
7170
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css" />

_includes/i18n-notice.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
{% assign notice_link_text = site.data.en.general.i18n_notice_link_text %}
88
{% endif %}
99
<p>{{ notice }} <a href="{{ page.url | replace: lang_path, '/en/' }}">{{ notice_link_text}}</a>.</p>
10-
<div id="close-i18n-notice-box" title="Close"></div>
10+
<div id="close-i18n-notice-box" title="Close" role="button" aria-label="Close notice" tabindex="0"></div>

_layouts/home.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
<div id="overlay"></div>
1515

1616
{% if page.lang != 'en' %}
17-
<div id="i18n-notice-box" class="doc-notice">
18-
{% include i18n-notice.html %}
17+
<div id="i18n-notice-box" class="doc-notice" hidden>
18+
{% include i18n-notice.html %}
1919
</div>
2020
{% endif %}
2121

_layouts/page.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<div id="overlay"></div>
1616

1717
{% if page.lang != 'en' %}
18-
<div id="i18n-notice-box" class="doc-notice">
18+
<div id="i18n-notice-box" class="doc-notice" hidden>
1919
{% include i18n-notice.html %}
2020
</div>
2121
{% endif %}

css/style.css

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ pre:has(code) button.failed {
474474
}
475475
}
476476

477-
/* top button */
477+
/* scroll to top button */
478478

479479
.scroll #top {
480480
opacity: .5;
@@ -611,20 +611,30 @@ table ul {
611611
font-weight: 600;
612612
}
613613

614+
/* i18n box */
614615
.doc-notice {
615616
padding-block: 1rem;
616617
padding-inline: 2.5rem;
617618
color: var(--box-fg);
618619
border-radius: 0 6px 6px 0;
619620
background: var(--notice-bg);
620621
border-left: 3px solid var(--notice-accent);
622+
margin-inline: auto;
623+
margin-block-start: 2rem;
624+
position: relative;
625+
grid-area: i18n;
626+
display: block;
621627
}
622628

623629
.doc-notice a{
624630
color: var(--notice-accent);
625631
text-decoration: underline;
626632
}
627633

634+
.doc-notice[hidden] {
635+
display: none;
636+
}
637+
628638
.doc-info {
629639
background: var(--info-bg);
630640
border-left: 3px solid var(--info-accent);
@@ -645,13 +655,6 @@ table ul {
645655
text-decoration: underline;
646656
}
647657

648-
#i18n-notice-box {
649-
margin-inline: auto;
650-
margin-block-start: 2rem;
651-
position: relative;
652-
grid-area: i18n;
653-
}
654-
655658
#close-i18n-notice-box {
656659
position: absolute;
657660
top: 3px;

js/app.js

Lines changed: 104 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,124 @@
1-
$(function(){
2-
var doc = $(document);
3-
4-
// top link
5-
$('#top').click(function(e){
6-
$('html, body').animate({scrollTop : 0}, 500);
7-
return false;
8-
});
1+
const languageElement = document.getElementById('languageData');
2+
const languagesData = languageElement ? JSON.parse(languageElement.dataset.languages) : [];
3+
const langDisplay = document.getElementById('current-lang');
4+
const i18nMsgBox = document.getElementById("i18n-notice-box");
5+
const scrollToTopBtn = document.getElementById("top");
6+
7+
// display current language in language picker component
8+
if (langDisplay) {
9+
const currentLanguage = window.location.pathname.split('/')[1];
10+
const matchedLang = languagesData.find(lang => lang.code === currentLanguage);
11+
langDisplay.textContent = matchedLang ? matchedLang.name : 'English';
12+
}
913

10-
// scrolling links
11-
var added;
12-
doc.scroll(function(e){
13-
if (doc.scrollTop() > 5) {
14-
if (added) return;
15-
added = true;
16-
$('body').addClass('scroll');
17-
} else {
18-
$('body').removeClass('scroll');
19-
added = false;
20-
}
14+
// scroll to top of the page
15+
if (scrollToTopBtn) {
16+
scrollToTopBtn.addEventListener("click", function (e) {
17+
e.preventDefault();
18+
window.scrollTo({
19+
top: 0,
20+
behavior: "smooth"
21+
})
2122
})
23+
}
2224

23-
// menu bar
24-
25-
var headings = $('h2, h3').map(function(i, el){
26-
return {
27-
top: $(el).offset().top - 200,
28-
id: el.id
29-
}
30-
});
31-
32-
function closest() {
33-
var h;
34-
var top = $(window).scrollTop();
35-
var i = headings.length;
36-
while (i--) {
37-
h = headings[i];
38-
if (top >= h.top) return h;
25+
// add/remove class 'scroll' on scroll by 5px
26+
const scrollTarget = document.querySelector('.logo-container');
27+
const scrollObserver = new IntersectionObserver(
28+
([entry]) => {
29+
if (!entry.isIntersecting) {
30+
document.body.classList.add('scroll');
31+
} else {
32+
document.body.classList.remove('scroll');
3933
}
34+
},
35+
{
36+
root: null,
37+
threshold: 0,
38+
rootMargin: '0px 0px 0px 0px'
4039
}
41-
42-
var currentApiPrefix;
43-
var parentMenuSelector;
44-
var lastApiPrefix;
45-
46-
if (document.readyState !== 'loading') {
47-
const languageElement = document.getElementById('languageData');
48-
const languagesData = languageElement ? JSON.parse(languageElement.dataset.languages) : [];
49-
50-
const langDisplay = document.getElementById('current-lang');
51-
52-
if (langDisplay) {
53-
const currentLanguage = window.location.pathname.split('/')[1];
54-
const matchedLang = languagesData.find(lang => lang.code === currentLanguage);
55-
langDisplay.textContent = matchedLang ? matchedLang.name : 'English';
56-
}
57-
}
58-
59-
$(document).scroll(function() {
60-
var h = closest();
61-
if (!h) return;
62-
63-
currentApiPrefix = h.id.split('.')[0];
64-
parentMenuSelector = '#'+ currentApiPrefix + '-menu';
65-
66-
$(parentMenuSelector).addClass('active');
67-
68-
if (lastApiPrefix && (lastApiPrefix != currentApiPrefix)) {
69-
$('#'+ lastApiPrefix + '-menu').removeClass('active');
40+
);
41+
42+
if (scrollTarget) scrollObserver.observe(scrollTarget);
43+
44+
// heighlight current Menu on scroll
45+
const headings = Array.from(document.querySelectorAll("h2, h3"));
46+
const menuLinks = document.querySelectorAll("#menu li a");
47+
48+
const observerOptions = {
49+
root: null,
50+
rootMargin: "-10% 0px -65% 0px",
51+
threshold: 1,
52+
};
53+
54+
const menuObserver = new IntersectionObserver((entries) => {
55+
entries.forEach((entry) => {
56+
if (entry.isIntersecting) {
57+
const currentApiPrefix = entry.target.id.split(".")[0];
58+
const parentMenuSelector = `#${currentApiPrefix}-menu`;
59+
const parentMenuEl = document.querySelector(parentMenuSelector);
60+
61+
// open submenu on scroll
62+
if (parentMenuEl) parentMenuEl.classList.add("active");
63+
64+
// Remove active class from last menu item
65+
const lastActiveMenu = document.querySelector(".active[id$='-menu']");
66+
if (lastActiveMenu && lastActiveMenu.id !== parentMenuEl.id) {
67+
lastActiveMenu.classList.remove("active");
68+
}
69+
70+
// Update active link
71+
menuLinks.forEach((link) => link.classList.remove("active"));
72+
const activeLink = document.querySelector(`a[href="#${entry.target.id}"]`);
73+
if (activeLink && !activeLink.classList.contains("active")) activeLink.classList.add("active");
7074
}
71-
72-
$('#menu li a').removeClass('active');
73-
74-
var a = $('a[href="#' + h.id + '"]');
75-
a.addClass('active');
76-
77-
lastApiPrefix = currentApiPrefix.split('.')[0];
78-
})
79-
80-
// i18n notice
81-
if (readCookie('i18nClose')) {
82-
$('#i18n-notice-box').hide();
83-
$("#i18n-notice-box").addClass("hidden");
84-
}
85-
else {
86-
$('#close-i18n-notice-box').on('click', function () {
87-
$('#i18n-notice-box').hide();
88-
$("#i18n-notice-box").addClass("hidden");
75+
});
76+
}, observerOptions);
77+
78+
headings.forEach((heading) => menuObserver.observe(heading));
79+
80+
// i18n message box : this box appears hidden for all page.lang != 'en'
81+
const isI18nCookie = readCookie('i18nClose');
82+
if (i18nMsgBox && !isI18nCookie) {
83+
const closeI18nBtn = document.getElementById("close-i18n-notice-box");
84+
// show notice box
85+
i18nMsgBox.hidden = false;
86+
// close notice box
87+
if (closeI18nBtn) {
88+
closeI18nBtn.addEventListener("click", () => {
89+
// hide notice
90+
i18nMsgBox.hidden = true;
91+
// set session cookie
8992
createCookie('i18nClose', 1);
90-
})
93+
});
94+
95+
// keyboard a11y
96+
closeI18nBtn.addEventListener("keydown", (e) => {
97+
if (e.key === "Enter" || e.key === " ") {
98+
e.preventDefault();
99+
closeI18nBtn.click();
100+
}
101+
});
91102
}
92-
})
93-
94-
103+
};
95104

96105
function createCookie(name, value, days) {
97-
var expires;
98-
106+
let expires = "";
99107
if (days) {
100-
var date = new Date();
101-
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
102-
expires = "; expires=" + date.toGMTString();
103-
} else {
104-
expires = "";
108+
const date = new Date();
109+
date.setTime(date.getTime() + (days * 864e5));
110+
expires = "; expires=" + date.toUTCString();
105111
}
106-
document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/";
112+
document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}${expires}; path=/; SameSite=Lax; Secure`;
107113
}
108114

109115
function readCookie(name) {
110-
var nameEQ = encodeURIComponent(name) + "=";
111-
var ca = document.cookie.split(';');
112-
for (var i = 0; i < ca.length; i++) {
113-
var c = ca[i];
116+
const nameEQ = encodeURIComponent(name) + "=";
117+
const ca = document.cookie.split(';');
118+
for (let i = 0; i < ca.length; i++) {
119+
let c = ca[i];
114120
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
115121
if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length));
116122
}
117123
return null;
118124
}
119-
120-
function eraseCookie(name) {
121-
createCookie(name, "", -1);
122-
}

0 commit comments

Comments
 (0)