Skip to content

Commit 9d78242

Browse files
committed
Improve scroll locking in the rustdoc 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.
1 parent 9a1f52d commit 9d78242

File tree

5 files changed

+100
-8
lines changed

5 files changed

+100
-8
lines changed

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

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,7 @@ function loadCss(cssFileName) {
382382

383383
function onHashChange(ev) {
384384
// If we're in mobile mode, we should hide the sidebar in any case.
385-
const sidebar = document.getElementsByClassName("sidebar")[0];
386-
removeClass(sidebar, "shown");
385+
hideSidebar();
387386
handleHashes(ev);
388387
}
389388

@@ -764,11 +763,50 @@ function loadCss(cssFileName) {
764763
});
765764
}());
766765

766+
let oldSidebarScrollPosition = null;
767+
768+
function showSidebar() {
769+
if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
770+
// This is to keep the scroll position on mobile.
771+
oldSidebarScrollPosition = window.scrollY;
772+
document.body.style.width = `${document.body.offsetWidth}px`;
773+
document.body.style.position = "fixed";
774+
document.body.style.top = `-${oldSidebarScrollPosition}px`;
775+
document.querySelector(".mobile-topbar").style.top = `${oldSidebarScrollPosition}px`;
776+
document.querySelector(".mobile-topbar").style.position = "relative";
777+
} else {
778+
oldSidebarScrollPosition = null;
779+
}
780+
const sidebar = document.getElementsByClassName("sidebar")[0];
781+
addClass(sidebar, "shown");
782+
}
783+
767784
function hideSidebar() {
785+
if (oldSidebarScrollPosition !== null) {
786+
// This is to keep the scroll position on mobile.
787+
document.body.style.width = "";
788+
document.body.style.position = "";
789+
document.body.style.top = "";
790+
document.querySelector(".mobile-topbar").style.top = "";
791+
document.querySelector(".mobile-topbar").style.position = "";
792+
// The scroll position is lost when resetting the style, hence why we store it in
793+
// `oldSidebarScrollPosition`.
794+
window.scrollTo(0, oldSidebarScrollPosition);
795+
oldSidebarScrollPosition = null;
796+
}
768797
const sidebar = document.getElementsByClassName("sidebar")[0];
769798
removeClass(sidebar, "shown");
770799
}
771800

801+
window.addEventListener("resize", () => {
802+
if (window.innerWidth >= window.RUSTDOC_MOBILE_BREAKPOINT &&
803+
oldSidebarScrollPosition !== null) {
804+
// If the user opens the sidebar in "mobile" mode, and then grows the browser window,
805+
// we need to switch away from mobile mode and make the main content area scrollable.
806+
hideSidebar();
807+
}
808+
});
809+
772810
function handleClick(id, f) {
773811
const elem = document.getElementById(id);
774812
if (elem) {
@@ -811,9 +849,9 @@ function loadCss(cssFileName) {
811849
sidebar_menu_toggle.addEventListener("click", () => {
812850
const sidebar = document.getElementsByClassName("sidebar")[0];
813851
if (!hasClass(sidebar, "shown")) {
814-
addClass(sidebar, "shown");
852+
showSidebar();
815853
} else {
816-
removeClass(sidebar, "shown");
854+
hideSidebar();
817855
}
818856
});
819857
}

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

Lines changed: 18 additions & 4 deletions
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 createDirEntry(elem, parent, fullPath, hasFoundFile) {
1616
const name = document.createElement("div");
@@ -66,30 +66,44 @@ function createDirEntry(elem, parent, fullPath, hasFoundFile) {
6666
function toggleSidebar() {
6767
const child = this.children[0];
6868
if (child.innerText === ">") {
69-
if (window.innerWidth < 701) {
69+
if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
7070
// This is to keep the scroll position on mobile.
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 < 701) {
81+
if (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";

src/librustdoc/html/static/js/storage.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const darkThemes = ["dark", "ayu"];
99
window.currentTheme = document.getElementById("themeStyle");
1010
window.mainTheme = document.getElementById("mainThemeStyle");
1111

12+
window.RUSTDOC_MOBILE_BREAKPOINT = 701;
13+
1214
const settingsDataset = (function() {
1315
const settingsElement = document.getElementById("default-settings");
1416
if (settingsElement === null) {
Lines changed: 31 additions & 0 deletions
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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,10 @@ click: "#sidebar-toggle"
152152
wait-for-css: (".sidebar", {"width": "0px"})
153153
// The "scrollTop" property should be the same.
154154
assert-window-property: {"pageYOffset": "2519"}
155+
156+
// We now check that the scroll position is restored if the window is resized.
157+
click: "#sidebar-toggle"
158+
wait-for-css: (".sidebar", {"width": "500px"})
159+
assert-window-property: {"pageYOffset": "0"}
160+
size: (900, 900)
161+
assert-window-property: {"pageYOffset": "2519"}

0 commit comments

Comments
 (0)