diff options
author | Kohei Yoshino <kohei.yoshino@gmail.com> | 2018-01-08 15:22:45 +0100 |
---|---|---|
committer | Dylan William Hardison <dylan@hardison.net> | 2018-01-08 15:22:45 +0100 |
commit | f86567c1f22f5d4dfa9bcc097efaef3ecb8b44bc (patch) | |
tree | f760021037129b07d8fe2565e4972e64eab014fb /js | |
parent | 19e368ee6d070699e52c845c324143568f08444a (diff) | |
download | bugzilla-f86567c1f22f5d4dfa9bcc097efaef3ecb8b44bc.tar.gz bugzilla-f86567c1f22f5d4dfa9bcc097efaef3ecb8b44bc.tar.xz |
Bug 1427800 - Wrong anchor scrolling with old UI
Diffstat (limited to 'js')
-rw-r--r-- | js/global.js | 58 |
1 files changed, 50 insertions, 8 deletions
diff --git a/js/global.js b/js/global.js index 93f364c9e..68aceb03f 100644 --- a/js/global.js +++ b/js/global.js @@ -214,22 +214,64 @@ const detect_blocked_gravatars = () => { } /** - * If the URL contains a hash like #c10, scroll down the page to show the - * element below the fixed global header. This workaround is required for - * comments on show_bug.cgi, components on describecomponents.cgi, etc. + * If the current URL contains a hash like `#c10`, adjust the scroll position to + * make some room above the focused element. */ -const scroll_element_into_view = () => { +const adjust_scroll_onload = () => { if (location.hash) { - const $main = document.querySelector('main'); const $target = document.querySelector(location.hash); if ($target) { - window.setTimeout(() => $main.scrollTop = $target.offsetTop - 20, 50); + window.setTimeout(() => scroll_element_into_view($target), 50); } } } +/** + * Bring an element into the visible area of the browser window. Unlike the + * native `Element.scrollIntoView()` function, this adds some extra room above + * the target element. Smooth scroll can be done using CSS. + * @param {Element} $target - An element to be brought. + * @param {Function} [complete] - An optional callback function to be executed + * once the scroll is complete. + */ +const scroll_element_into_view = ($target, complete) => { + let top = 0; + let $element = $target; + + // Traverse up in the DOM tree to the scroll container of the + // focused element, either `<main>` or `<div role="feed">`. + do { + top += ($element.offsetTop || 0); + $element = $element.offsetParent; + } while ($element && !$element.matches('main, [role="feed"]')) + + if (!$element) { + return; + } + + 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); + } + + $element.scrollTop = top - 20; +} + window.addEventListener('DOMContentLoaded', focus_main_content, { once: true }); window.addEventListener('load', detect_blocked_gravatars, { once: true }); -window.addEventListener('load', scroll_element_into_view, { once: true }); -window.addEventListener('hashchange', scroll_element_into_view); +window.addEventListener('load', adjust_scroll_onload, { once: true }); +window.addEventListener('hashchange', adjust_scroll_onload); |