mirror of
https://github.com/facebook/docusaurus.git
synced 2025-07-30 15:00:09 +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"
|
aria-hidden="true"
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
className={clsx('anchor', `anchor__${Tag}`, {
|
className={clsx('anchor', `anchor__${Tag}`, {
|
||||||
[styles.enhancedAnchor]: !hideOnScroll,
|
[styles.anchorWithHideOnScrollNavbar]: hideOnScroll,
|
||||||
|
[styles.anchorWithStickyNavbar]: !hideOnScroll,
|
||||||
})}
|
})}
|
||||||
id={id}
|
id={id}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -5,6 +5,16 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* 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 {Params} from '@theme/hooks/useTOCHighlight';
|
||||||
import {useEffect, useRef} from 'react';
|
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
|
// 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 {
|
function getVisibleBoundingClientRect(element: HTMLElement): DOMRect {
|
||||||
|
@ -30,11 +31,13 @@ function getAnchors() {
|
||||||
return Array.from(document.querySelectorAll(selector)) as HTMLElement[];
|
return Array.from(document.querySelectorAll(selector)) as HTMLElement[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActiveAnchor(): Element | null {
|
function getActiveAnchor({
|
||||||
|
anchorTopOffset,
|
||||||
|
}: {
|
||||||
|
anchorTopOffset: number;
|
||||||
|
}): Element | null {
|
||||||
const anchors = getAnchors();
|
const anchors = getAnchors();
|
||||||
|
|
||||||
const anchorTopOffset = 100; // Skip anchors that are too close to the viewport top
|
|
||||||
|
|
||||||
// Naming is hard
|
// Naming is hard
|
||||||
// The "nextVisibleAnchor" is the first anchor that appear under the viewport top boundary
|
// 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
|
// 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[];
|
) 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 {
|
function useTOCHighlight(params: Params): void {
|
||||||
const lastActiveLinkRef = useRef<HTMLAnchorElement | undefined>(undefined);
|
const lastActiveLinkRef = useRef<HTMLAnchorElement | undefined>(undefined);
|
||||||
|
|
||||||
|
const anchorTopOffsetRef = useAnchorTopOffsetRef();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const {linkClassName, linkActiveClassName} = params;
|
const {linkClassName, linkActiveClassName} = params;
|
||||||
|
|
||||||
|
@ -94,7 +118,9 @@ function useTOCHighlight(params: Params): void {
|
||||||
|
|
||||||
function updateActiveLink() {
|
function updateActiveLink() {
|
||||||
const links = getLinks(linkClassName);
|
const links = getLinks(linkClassName);
|
||||||
const activeAnchor = getActiveAnchor();
|
const activeAnchor = getActiveAnchor({
|
||||||
|
anchorTopOffset: anchorTopOffsetRef.current,
|
||||||
|
});
|
||||||
const activeLink = links.find(
|
const activeLink = links.find(
|
||||||
(link) => activeAnchor && activeAnchor.id === getLinkAnchorValue(link),
|
(link) => activeAnchor && activeAnchor.id === getLinkAnchorValue(link),
|
||||||
);
|
);
|
||||||
|
@ -113,7 +139,7 @@ function useTOCHighlight(params: Params): void {
|
||||||
document.removeEventListener('scroll', updateActiveLink);
|
document.removeEventListener('scroll', updateActiveLink);
|
||||||
document.removeEventListener('resize', updateActiveLink);
|
document.removeEventListener('resize', updateActiveLink);
|
||||||
};
|
};
|
||||||
}, [params]);
|
}, [params, anchorTopOffsetRef]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useTOCHighlight;
|
export default useTOCHighlight;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue