Material UI粘性列表滚动时同步选中导航按钮问题求助
The core issue with your original implementation is that @makotot/ghostui's Scrollspy listens to window scroll events, but your scrollable content is contained within a Material UI List (with overflow: auto), so scroll events don't bubble up to the window. Here's a custom implementation that fixes this:
Modified Code
import * as React from "react"; import Grid from "@mui/material/Grid"; import List from "@mui/material/List"; import ListItem from "@mui/material/ListItem"; import ListItemText from "@mui/material/ListItemText"; import ListSubheader from "@mui/material/ListSubheader"; import ListItemButton from "@mui/material/ListItemButton"; import Divider from "@mui/material/Divider"; import { map } from "lodash"; const categories = [0, 1, 2, 3, 4]; const SCROLL_OFFSET = 100; // Adjust this to match your sticky header offset export default function PinnedSubheaderList() { const sectionRefs = React.useMemo( () => categories.map(() => React.createRef<HTMLLIElement>()), [] ); const scrollContainerRef = React.useRef<HTMLUListElement>(null); const [selected, setSelected] = React.useState<number>(0); const handleChangeCategory = (categoryIndex: number): void => { const targetSection = sectionRefs[categoryIndex].current; if (targetSection && scrollContainerRef.current) { scrollContainerRef.current.scrollTo({ top: targetSection.offsetTop - SCROLL_OFFSET, behavior: "smooth" }); setSelected(categoryIndex); } }; React.useEffect(() => { const container = scrollContainerRef.current; if (!container) return; const handleScroll = () => { const scrollTop = container.scrollTop; // Iterate from bottom to top to find the first visible section for (let i = sectionRefs.length - 1; i >= 0; i--) { const section = sectionRefs[i].current; if (!section) continue; const sectionTop = section.offsetTop; const sectionBottom = sectionTop + section.offsetHeight; // Check if section is within the visible area (adjusted for offset) if (sectionTop <= scrollTop + SCROLL_OFFSET && sectionBottom > scrollTop) { setSelected(i); break; } } }; container.addEventListener("scroll", handleScroll); return () => container.removeEventListener("scroll", handleScroll); }, []); return ( <Grid container spacing={2}> <Grid item md={6} xs={4}> <List sx={{ border: `1px solid grey`, my: 2 }} dense disablePadding data-cy="nav-wrapper" > {map(categories, (category, index) => ( <React.Fragment key={category}> <ListItem disablePadding data-cy={`nav-item`}> <ListItemButton selected={selected === index} onClick={() => handleChangeCategory(index)} > <ListItemText primary={category} /> </ListItemButton> </ListItem> <Divider /> </React.Fragment> ))} </List> </Grid> <Grid item md={6} xs={4}> <List ref={scrollContainerRef} sx={{ width: "100%", bgcolor: "background.paper", position: "relative", overflow: "auto", maxHeight: 300, "& ul": { padding: 0 } }} subheader={<li />} data-cy="section-wrapper" > {categories.map((sectionId, sectionIndex) => ( <li key={`section-${sectionId}`} ref={sectionRefs[sectionIndex]}> <ul> <ListSubheader>{`I'm sticky ${sectionId}`}</ListSubheader> {[0, 1, 2].map((item) => ( <ListItem key={`item-${sectionId}-${item}`}> <ListItemText primary={`Item ${item}`} /> </ListItem> ))} </ul> </li> ))} </List> </Grid> </Grid> ); }
Key Changes Explained
- Custom Scroll Listener: Added a scroll event listener directly to the scrollable List container (instead of the window) to capture scroll events from your sticky list.
- Section Ref Adjustment: Updated
sectionRefsto target the outer<li>elements (the actual section containers) instead of inner<ul>elements, ensuring accurate visibility checks. - Visibility Detection: Implemented logic to find the section that's within the visible area of the scroll container, adjusted for your offset value.
- Smooth Scroll Control: Replaced
scrollIntoViewwithscrollToon the container for better control over scroll positioning with the offset.
Notes
- Adjust
SCROLL_OFFSETto match the height of your sticky header or desired scroll position offset. - The scroll handler iterates from bottom to top to ensure the correct section is selected when multiple sections are partially visible.
内容的提问来源于stack exchange,提问作者arunmmanoharan
相关产品推荐
相关产品推荐

