diff options
-rw-r--r-- | js/global.js | 63 |
1 files changed, 46 insertions, 17 deletions
diff --git a/js/global.js b/js/global.js index 68aceb03f..10593fb3b 100644 --- a/js/global.js +++ b/js/global.js @@ -192,13 +192,47 @@ $().ready(function() { }); /** + * Emulate the `scrollend` event on an HTML element that will be fired once a + * certain amount of scroll is complete. + * @param {Element} $target - An element to be observed. + */ +const emulate_scrollend_event = $target => { + if ('onscrollend' in $target) { + return; + } + + let timer; + + $target.addEventListener('scroll', () => { + window.requestAnimationFrame(() => { + window.clearTimeout(timer); + timer = window.setTimeout(() => $target.dispatchEvent(new UIEvent('scrollend')), 100); + }); + }); + + $target.onscrollend = null; +} + +/** * Focus the main content when the page is loaded and there is no autofocus * element, so the user can immediately scroll down the page using keyboard. + * Also, remember the scroll position on `<main>` so it can be restored when + * `location.hash` is changed. + * @see adjust_scroll_onload */ const focus_main_content = () => { + const $main = document.querySelector('main'); + if (!document.querySelector('[autofocus]')) { - document.querySelector('main').focus(); + $main.focus(); } + + emulate_scrollend_event($main); + + $main.addEventListener('scrollend', () => { + history.replaceState(Object.assign(history.state || {}, { main_scroll_top: $main.scrollTop }), + document.title, location.href); + }); } /** @@ -215,7 +249,9 @@ const detect_blocked_gravatars = () => { /** * If the current URL contains a hash like `#c10`, adjust the scroll position to - * make some room above the focused element. + * make some room above the focused element. Or, restore the scroll position on + * `<main>` if remembered. + * @see focus_main_content */ const adjust_scroll_onload = () => { if (location.hash) { @@ -224,6 +260,12 @@ const adjust_scroll_onload = () => { if ($target) { window.setTimeout(() => scroll_element_into_view($target), 50); } + } else if (history.state) { + const main_scroll_top = history.state.main_scroll_top; + + if (typeof main_scroll_top === "number") { + document.querySelector('main').scrollTop = main_scroll_top; + } } } @@ -251,21 +293,8 @@ const scroll_element_into_view = ($target, complete) => { } if (typeof complete === 'function') { - const callback = () => { - $element.removeEventListener('scroll', listener); - complete(); - }; - - // Emulate the `scrollend` event - const listener = () => { - window.clearTimeout(timer); - timer = window.setTimeout(callback, 100); - }; - - // Make sure the callback is always fired even if no scroll happened - let timer = window.setTimeout(callback, 100); - - $element.addEventListener('scroll', listener); + emulate_scrollend_event($element); + $element.addEventListener('scrollend', () => complete(), true); } $element.scrollTop = top - 20; |