1
0
mirror of https://github.com/prometheus/docs.git synced 2026-02-05 15:45:27 +01:00

Fix scrolling to fragment identifiers on newly loaded large pages (#2788)

When following a link to a fragment (#foo) on a long documentation page,
the browser would try to scroll to the fragment too early, before some
additional client-side layout shifts (could be due to font loading,
highlighting, etc.) would cause the scroll position to be wrong again. This
component waits for the layout to settle and then manually scrolls to the
requested fragment again.

You can see the problem by clicking on:

https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config

It usually does not scroll directly to the relabeling section, but
somewhere above it.

Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
Julius Volz
2025-12-02 11:16:15 +01:00
committed by GitHub
parent 6b9ca93373
commit 48c5bbe57b
2 changed files with 33 additions and 0 deletions

View File

@@ -11,6 +11,7 @@ import {
import TOC from "@/components/TOC";
import LeftNav from "./LeftNav";
import { IconMenu2 } from "@tabler/icons-react";
import { AnchorScroller } from "@/components/AnchorScroller";
export default function DocsLayout({
children,
@@ -85,6 +86,7 @@ export default function DocsLayout({
}}
/>
</Group>
<AnchorScroller />
</>
);
}

View File

@@ -0,0 +1,31 @@
"use client";
import { useEffect } from "react";
// A component that, when included on a page, scrolls to the element
// indicated by the URL hash (if any) after the page loads. While the
// browser normally does this automatically, it does so too early in
// our case, before the layout settles, resulting in incorrect scroll
// positions.
export function AnchorScroller() {
useEffect(() => {
const hash = window.location.hash;
if (!hash) {
return;
}
const el = document.querySelector(hash);
if (!el) {
return;
}
// Wait for layout to settle using two wrapped requestAnimationFrame calls.
requestAnimationFrame(() => {
requestAnimationFrame(() => {
el.scrollIntoView({ block: "start" });
});
});
}, []);
return null;
}