diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 0702b2b0b7c..d5f9ee5724f 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -348,8 +348,7 @@ function loadCss(cssFileName) {
function onHashChange(ev) {
// If we're in mobile mode, we should hide the sidebar in any case.
- const sidebar = document.getElementsByClassName("sidebar")[0];
- removeClass(sidebar, "shown");
+ hideSidebar();
handleHashes(ev);
}
@@ -728,11 +727,50 @@ function loadCss(cssFileName) {
});
}());
+ let oldSidebarScrollPosition = null;
+
+ function showSidebar() {
+ if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
+ // This is to keep the scroll position on mobile.
+ oldSidebarScrollPosition = window.scrollY;
+ document.body.style.width = `${document.body.offsetWidth}px`;
+ document.body.style.position = "fixed";
+ document.body.style.top = `-${oldSidebarScrollPosition}px`;
+ document.querySelector(".mobile-topbar").style.top = `${oldSidebarScrollPosition}px`;
+ document.querySelector(".mobile-topbar").style.position = "relative";
+ } else {
+ oldSidebarScrollPosition = null;
+ }
+ const sidebar = document.getElementsByClassName("sidebar")[0];
+ addClass(sidebar, "shown");
+ }
+
function hideSidebar() {
+ if (oldSidebarScrollPosition !== null) {
+ // This is to keep the scroll position on mobile.
+ document.body.style.width = "";
+ document.body.style.position = "";
+ document.body.style.top = "";
+ document.querySelector(".mobile-topbar").style.top = "";
+ document.querySelector(".mobile-topbar").style.position = "";
+ // The scroll position is lost when resetting the style, hence why we store it in
+ // `oldSidebarScrollPosition`.
+ window.scrollTo(0, oldSidebarScrollPosition);
+ oldSidebarScrollPosition = null;
+ }
const sidebar = document.getElementsByClassName("sidebar")[0];
removeClass(sidebar, "shown");
}
+ window.addEventListener("resize", () => {
+ if (window.innerWidth >= window.RUSTDOC_MOBILE_BREAKPOINT &&
+ oldSidebarScrollPosition !== null) {
+ // If the user opens the sidebar in "mobile" mode, and then grows the browser window,
+ // we need to switch away from mobile mode and make the main content area scrollable.
+ hideSidebar();
+ }
+ });
+
function handleClick(id, f) {
const elem = document.getElementById(id);
if (elem) {
@@ -775,9 +813,9 @@ function loadCss(cssFileName) {
sidebar_menu_toggle.addEventListener("click", () => {
const sidebar = document.getElementsByClassName("sidebar")[0];
if (!hasClass(sidebar, "shown")) {
- addClass(sidebar, "shown");
+ showSidebar();
} else {
- removeClass(sidebar, "shown");
+ hideSidebar();
}
});
}
diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js
index c45d614293a..06d15d9e5ff 100644
--- a/src/librustdoc/html/static/js/source-script.js
+++ b/src/librustdoc/html/static/js/source-script.js
@@ -10,7 +10,7 @@
(function() {
const rootPath = document.getElementById("rustdoc-vars").attributes["data-root-path"].value;
-let oldScrollPosition = 0;
+let oldScrollPosition = null;
const NAME_OFFSET = 0;
const DIRS_OFFSET = 1;
@@ -75,18 +75,21 @@ function toggleSidebar() {
oldScrollPosition = window.scrollY;
document.body.style.position = "fixed";
document.body.style.top = `-${oldScrollPosition}px`;
+ } else {
+ oldScrollPosition = null;
}
addClass(document.documentElement, "source-sidebar-expanded");
child.innerText = "<";
updateLocalStorage("source-sidebar-show", "true");
} else {
- if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
+ if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT && oldScrollPosition !== null) {
// This is to keep the scroll position on mobile.
document.body.style.position = "";
document.body.style.top = "";
// The scroll position is lost when resetting the style, hence why we store it in
- // `oldScroll`.
+ // `oldScrollPosition`.
window.scrollTo(0, oldScrollPosition);
+ oldScrollPosition = null;
}
removeClass(document.documentElement, "source-sidebar-expanded");
child.innerText = ">";
@@ -94,6 +97,17 @@ function toggleSidebar() {
}
}
+window.addEventListener("resize", () => {
+ if (window.innerWidth >= window.RUSTDOC_MOBILE_BREAKPOINT && oldScrollPosition !== null) {
+ // If the user opens the sidebar in "mobile" mode, and then grows the browser window,
+ // we need to switch away from mobile mode and make the main content area scrollable.
+ document.body.style.position = "";
+ document.body.style.top = "";
+ window.scrollTo(0, oldScrollPosition);
+ oldScrollPosition = null;
+ }
+});
+
function createSidebarToggle() {
const sidebarToggle = document.createElement("div");
sidebarToggle.id = "sidebar-toggle";
diff --git a/src/test/rustdoc-gui/sidebar-mobile-scroll.goml b/src/test/rustdoc-gui/sidebar-mobile-scroll.goml
new file mode 100644
index 00000000000..b3bcea25338
--- /dev/null
+++ b/src/test/rustdoc-gui/sidebar-mobile-scroll.goml
@@ -0,0 +1,31 @@
+// This test ensures that the mobile sidebar preserves scroll position.
+goto: file://|DOC_PATH|/test_docs/struct.Foo.html
+// Switching to "mobile view" by reducing the width to 600px.
+size: (600, 600)
+assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
+
+// Scroll down.
+scroll-to: "//h2[@id='blanket-implementations']"
+assert-window-property: {"pageYOffset": "702"}
+
+// Open the sidebar menu.
+click: ".sidebar-menu-toggle"
+wait-for-css: (".sidebar", {"left": "0px"})
+
+// We are no longer "scrolled". It's important that the user can't
+// scroll the body at all, but these test scripts are run only in Chrome,
+// and we need to use a more complicated solution to this problem because
+// of Mobile Safari...
+assert-window-property: {"pageYOffset": "0"}
+
+// Close the sidebar menu. Make sure the scroll position gets restored.
+click: ".sidebar-menu-toggle"
+wait-for-css: (".sidebar", {"left": "-1000px"})
+assert-window-property: {"pageYOffset": "702"}
+
+// Now test that scrollability returns when the browser window is just resized.
+click: ".sidebar-menu-toggle"
+wait-for-css: (".sidebar", {"left": "0px"})
+assert-window-property: {"pageYOffset": "0"}
+size: (900, 900)
+assert-window-property: {"pageYOffset": "702"}
diff --git a/src/test/rustdoc-gui/sidebar-source-code-display.goml b/src/test/rustdoc-gui/sidebar-source-code-display.goml
index fa322574fde..e4662a10ed5 100644
--- a/src/test/rustdoc-gui/sidebar-source-code-display.goml
+++ b/src/test/rustdoc-gui/sidebar-source-code-display.goml
@@ -233,6 +233,17 @@ wait-for-css: (".sidebar", {"width": "0px"})
// The "scrollTop" property should be the same.
assert-window-property: {"pageYOffset": "2519"}
+// We now check that the scroll position is restored if the window is resized.
+size: (500, 700)
+click: "#sidebar-toggle"
+wait-for-css: ("#source-sidebar", {"visibility": "visible"})
+assert-window-property: {"pageYOffset": "0"}
+size: (900, 900)
+assert-window-property: {"pageYOffset": "2519"}
+size: (500, 700)
+click: "#sidebar-toggle"
+wait-for-css: ("#source-sidebar", {"visibility": "hidden"})
+
// We now check that opening the sidebar and clicking a link will close it.
// The behavior here on mobile is different than the behavior on desktop,
// but common sense dictates that if you have a list of files that fills the entire screen, and