Skip to content

Commit 6b60bc6

Browse files
committed
rustdoc: improve scroll locking in the rustdoc mobile sidebars
This commit ports the scroll locking behavior from the source sidebar to the regular sidebar, and also fixes some bad behavior where opening a "mobile" sidebar, and growing the viewport so that the "desktop" mode without scroll locking is activated, could potentially leave the page stuck. This does not affect the behavior on larger screens. Only small ones, where the sidebar covers up the main content.
1 parent 54f79ba commit 6b60bc6

File tree

4 files changed

+101
-7
lines changed

4 files changed

+101
-7
lines changed

src/librustdoc/html/static/js/main.js

+42-4
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,7 @@ function loadCss(cssFileName) {
348348

349349
function onHashChange(ev) {
350350
// If we're in mobile mode, we should hide the sidebar in any case.
351-
const sidebar = document.getElementsByClassName("sidebar")[0];
352-
removeClass(sidebar, "shown");
351+
hideSidebar();
353352
handleHashes(ev);
354353
}
355354

@@ -731,11 +730,50 @@ function loadCss(cssFileName) {
731730
});
732731
}());
733732

733+
let oldSidebarScrollPosition = null;
734+
735+
function showSidebar() {
736+
if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
737+
// This is to keep the scroll position on mobile.
738+
oldSidebarScrollPosition = window.scrollY;
739+
document.body.style.width = `${document.body.offsetWidth}px`;
740+
document.body.style.position = "fixed";
741+
document.body.style.top = `-${oldSidebarScrollPosition}px`;
742+
document.querySelector(".mobile-topbar").style.top = `${oldSidebarScrollPosition}px`;
743+
document.querySelector(".mobile-topbar").style.position = "relative";
744+
} else {
745+
oldSidebarScrollPosition = null;
746+
}
747+
const sidebar = document.getElementsByClassName("sidebar")[0];
748+
addClass(sidebar, "shown");
749+
}
750+
734751
function hideSidebar() {
752+
if (oldSidebarScrollPosition !== null) {
753+
// This is to keep the scroll position on mobile.
754+
document.body.style.width = "";
755+
document.body.style.position = "";
756+
document.body.style.top = "";
757+
document.querySelector(".mobile-topbar").style.top = "";
758+
document.querySelector(".mobile-topbar").style.position = "";
759+
// The scroll position is lost when resetting the style, hence why we store it in
760+
// `oldSidebarScrollPosition`.
761+
window.scrollTo(0, oldSidebarScrollPosition);
762+
oldSidebarScrollPosition = null;
763+
}
735764
const sidebar = document.getElementsByClassName("sidebar")[0];
736765
removeClass(sidebar, "shown");
737766
}
738767

768+
window.addEventListener("resize", () => {
769+
if (window.innerWidth >= window.RUSTDOC_MOBILE_BREAKPOINT &&
770+
oldSidebarScrollPosition !== null) {
771+
// If the user opens the sidebar in "mobile" mode, and then grows the browser window,
772+
// we need to switch away from mobile mode and make the main content area scrollable.
773+
hideSidebar();
774+
}
775+
});
776+
739777
function handleClick(id, f) {
740778
const elem = document.getElementById(id);
741779
if (elem) {
@@ -778,9 +816,9 @@ function loadCss(cssFileName) {
778816
sidebar_menu_toggle.addEventListener("click", () => {
779817
const sidebar = document.getElementsByClassName("sidebar")[0];
780818
if (!hasClass(sidebar, "shown")) {
781-
addClass(sidebar, "shown");
819+
showSidebar();
782820
} else {
783-
removeClass(sidebar, "shown");
821+
hideSidebar();
784822
}
785823
});
786824
}

src/librustdoc/html/static/js/source-script.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
(function() {
1111

1212
const rootPath = document.getElementById("rustdoc-vars").attributes["data-root-path"].value;
13-
let oldScrollPosition = 0;
13+
let oldScrollPosition = null;
1414

1515
function closeSidebarIfMobile() {
1616
if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
@@ -71,25 +71,39 @@ function toggleSidebar() {
7171
oldScrollPosition = window.scrollY;
7272
document.body.style.position = "fixed";
7373
document.body.style.top = `-${oldScrollPosition}px`;
74+
} else {
75+
oldScrollPosition = null;
7476
}
7577
addClass(document.documentElement, "source-sidebar-expanded");
7678
child.innerText = "<";
7779
updateLocalStorage("source-sidebar-show", "true");
7880
} else {
79-
if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
81+
if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT && oldScrollPosition !== null) {
8082
// This is to keep the scroll position on mobile.
8183
document.body.style.position = "";
8284
document.body.style.top = "";
8385
// The scroll position is lost when resetting the style, hence why we store it in
84-
// `oldScroll`.
86+
// `oldScrollPosition`.
8587
window.scrollTo(0, oldScrollPosition);
88+
oldScrollPosition = null;
8689
}
8790
removeClass(document.documentElement, "source-sidebar-expanded");
8891
child.innerText = ">";
8992
updateLocalStorage("source-sidebar-show", "false");
9093
}
9194
}
9295

96+
window.addEventListener("resize", () => {
97+
if (window.innerWidth >= window.RUSTDOC_MOBILE_BREAKPOINT && oldScrollPosition !== null) {
98+
// If the user opens the sidebar in "mobile" mode, and then grows the browser window,
99+
// we need to switch away from mobile mode and make the main content area scrollable.
100+
document.body.style.position = "";
101+
document.body.style.top = "";
102+
window.scrollTo(0, oldScrollPosition);
103+
oldScrollPosition = null;
104+
}
105+
});
106+
93107
function createSidebarToggle() {
94108
const sidebarToggle = document.createElement("div");
95109
sidebarToggle.id = "sidebar-toggle";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// This test ensures that the mobile sidebar preserves scroll position.
2+
goto: file://|DOC_PATH|/test_docs/struct.Foo.html
3+
// Switching to "mobile view" by reducing the width to 600px.
4+
size: (600, 600)
5+
assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
6+
7+
// Scroll down.
8+
scroll-to: "//h2[@id='blanket-implementations']"
9+
assert-window-property: {"pageYOffset": "702"}
10+
11+
// Open the sidebar menu.
12+
click: ".sidebar-menu-toggle"
13+
wait-for-css: (".sidebar", {"left": "0px"})
14+
15+
// We are no longer "scrolled". It's important that the user can't
16+
// scroll the body at all, but these test scripts are run only in Chrome,
17+
// and we need to use a more complicated solution to this problem because
18+
// of Mobile Safari...
19+
assert-window-property: {"pageYOffset": "0"}
20+
21+
// Close the sidebar menu. Make sure the scroll position gets restored.
22+
click: ".sidebar-menu-toggle"
23+
wait-for-css: (".sidebar", {"left": "-1000px"})
24+
assert-window-property: {"pageYOffset": "702"}
25+
26+
// Now test that scrollability returns when the browser window is just resized.
27+
click: ".sidebar-menu-toggle"
28+
wait-for-css: (".sidebar", {"left": "0px"})
29+
assert-window-property: {"pageYOffset": "0"}
30+
size: (900, 900)
31+
assert-window-property: {"pageYOffset": "702"}

src/test/rustdoc-gui/sidebar-source-code-display.goml

+11
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,17 @@ wait-for-css: (".sidebar", {"width": "0px"})
233233
// The "scrollTop" property should be the same.
234234
assert-window-property: {"pageYOffset": "2519"}
235235

236+
// We now check that the scroll position is restored if the window is resized.
237+
size: (500, 700)
238+
click: "#sidebar-toggle"
239+
wait-for-css: ("#source-sidebar", {"visibility": "visible"})
240+
assert-window-property: {"pageYOffset": "0"}
241+
size: (900, 900)
242+
assert-window-property: {"pageYOffset": "2519"}
243+
size: (500, 700)
244+
click: "#sidebar-toggle"
245+
wait-for-css: ("#source-sidebar", {"visibility": "hidden"})
246+
236247
// We now check that opening the sidebar and clicking a link will close it.
237248
// The behavior here on mobile is different than the behavior on desktop,
238249
// but common sense dictates that if you have a list of files that fills the entire screen, and

0 commit comments

Comments
 (0)