mirror of
https://github.com/facebook/docusaurus.git
synced 2025-07-29 06:29:03 +02:00
fix: toc does not highlight clicked anchor + use scroll-margin-top (#5425)
* fix toc highlighting anchorTopOffset issues * fix comment * use ternary * revert to previous offset
This commit is contained in:
parent
1f1c7f1695
commit
2a72c64581
3 changed files with 45 additions and 8 deletions
|
@ -49,7 +49,8 @@ const createAnchorHeading = (
|
|||
aria-hidden="true"
|
||||
tabIndex={-1}
|
||||
className={clsx('anchor', `anchor__${Tag}`, {
|
||||
[styles.enhancedAnchor]: !hideOnScroll,
|
||||
[styles.anchorWithHideOnScrollNavbar]: hideOnScroll,
|
||||
[styles.anchorWithStickyNavbar]: !hideOnScroll,
|
||||
})}
|
||||
id={id}
|
||||
/>
|
||||
|
|
|
@ -5,6 +5,16 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
.enhancedAnchor {
|
||||
top: calc(var(--ifm-navbar-height) * -1 - 0.5rem);
|
||||
/*
|
||||
When the navbar is sticky, ensure that on anchor click,
|
||||
the browser does not scroll that anchor behind the navbar
|
||||
See https://twitter.com/JoshWComeau/status/1332015868725891076
|
||||
*/
|
||||
.anchorWithStickyNavbar {
|
||||
scroll-margin-top: calc(var(--ifm-navbar-height) + 0.5rem);
|
||||
/* top: calc(var(--ifm-navbar-height) * -1 - 0.5rem); */
|
||||
}
|
||||
|
||||
.anchorWithHideOnScrollNavbar {
|
||||
scroll-margin-top: 0.5rem;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import {Params} from '@theme/hooks/useTOCHighlight';
|
||||
import {useEffect, useRef} from 'react';
|
||||
import {useThemeConfig} from '@docusaurus/theme-common';
|
||||
|
||||
// If the anchor has no height and is just a "marker" in the dom; we'll use the parent (normally the link text) rect boundaries instead
|
||||
function getVisibleBoundingClientRect(element: HTMLElement): DOMRect {
|
||||
|
@ -30,11 +31,13 @@ function getAnchors() {
|
|||
return Array.from(document.querySelectorAll(selector)) as HTMLElement[];
|
||||
}
|
||||
|
||||
function getActiveAnchor(): Element | null {
|
||||
function getActiveAnchor({
|
||||
anchorTopOffset,
|
||||
}: {
|
||||
anchorTopOffset: number;
|
||||
}): Element | null {
|
||||
const anchors = getAnchors();
|
||||
|
||||
const anchorTopOffset = 100; // Skip anchors that are too close to the viewport top
|
||||
|
||||
// Naming is hard
|
||||
// The "nextVisibleAnchor" is the first anchor that appear under the viewport top boundary
|
||||
// Note: it does not mean this anchor is visible yet, but if user continues scrolling down, it will be the first to become visible
|
||||
|
@ -74,9 +77,30 @@ function getLinks(linkClassName: string) {
|
|||
) as HTMLAnchorElement[];
|
||||
}
|
||||
|
||||
function getNavbarHeight(): number {
|
||||
// Not ideal to obtain actual height this way
|
||||
// Using TS ! (not ?) because otherwise a bad selector would be un-noticed
|
||||
return document.querySelector('.navbar')!.clientHeight;
|
||||
}
|
||||
|
||||
function useAnchorTopOffsetRef() {
|
||||
const anchorTopOffsetRef = useRef<number>(0);
|
||||
const {
|
||||
navbar: {hideOnScroll},
|
||||
} = useThemeConfig();
|
||||
|
||||
useEffect(() => {
|
||||
anchorTopOffsetRef.current = hideOnScroll ? 0 : getNavbarHeight();
|
||||
}, [hideOnScroll]);
|
||||
|
||||
return anchorTopOffsetRef;
|
||||
}
|
||||
|
||||
function useTOCHighlight(params: Params): void {
|
||||
const lastActiveLinkRef = useRef<HTMLAnchorElement | undefined>(undefined);
|
||||
|
||||
const anchorTopOffsetRef = useAnchorTopOffsetRef();
|
||||
|
||||
useEffect(() => {
|
||||
const {linkClassName, linkActiveClassName} = params;
|
||||
|
||||
|
@ -94,7 +118,9 @@ function useTOCHighlight(params: Params): void {
|
|||
|
||||
function updateActiveLink() {
|
||||
const links = getLinks(linkClassName);
|
||||
const activeAnchor = getActiveAnchor();
|
||||
const activeAnchor = getActiveAnchor({
|
||||
anchorTopOffset: anchorTopOffsetRef.current,
|
||||
});
|
||||
const activeLink = links.find(
|
||||
(link) => activeAnchor && activeAnchor.id === getLinkAnchorValue(link),
|
||||
);
|
||||
|
@ -113,7 +139,7 @@ function useTOCHighlight(params: Params): void {
|
|||
document.removeEventListener('scroll', updateActiveLink);
|
||||
document.removeEventListener('resize', updateActiveLink);
|
||||
};
|
||||
}, [params]);
|
||||
}, [params, anchorTopOffsetRef]);
|
||||
}
|
||||
|
||||
export default useTOCHighlight;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue