我有一个使用 Tailwind CSS 的基于 React 的 NextJS 应用程序。有一个页面组件显示从 A 到 Ö(瑞典语 aplhabet)排序的人员列表。使用移动视图时,用户体验规定非字母字母列表应在单行上可见,并且可以使用箭头或手指在屏幕上进行缩放。
问题:在移动视图中,列表从字母 J 开始,而不是字母 A。可能是因为它跨越或溢出屏幕。用户无法滚动到字母 A(见图)。
问题:如何使字母表适合滚动距离,如何让它进一步滚动回到第一个字母,或者更好的是,让它从字母 A 开始?
我当前的 LetterMeny 组件代码。
'use client';
import Link from 'next/link';
import React, { useEffect, useRef, useState } from 'react';
import { cn } from '@/utils/classnames';
import NavigateIcon from './icons/NavigateIcon';
const Letters = [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
'å',
'ä',
'ö',
];
type Props = {
currentElementIndexInViewport: number;
activeLetters: string[];
};
const LetterMenu: React.FC<Props> = ({ currentElementIndexInViewport, activeLetters }) => {
const carousel = useRef<HTMLUListElement>(null);
const letterMenuRef = useRef<HTMLDivElement>(null);
const [isSticky, setIsSticky] = useState(false);
const [isAtStart, setIsAtStart] = useState(true);
const [isAtEnd, setIsAtEnd] = useState(false);
const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
const targetId = event.currentTarget.getAttribute('href')?.substring(1);
const targetElement = targetId ? document.getElementById(targetId) : null;
if (targetElement) {
targetElement.scrollIntoView({
behavior: 'smooth',
});
}
};
const updateButtonVisibility = () => {
if (carousel.current) {
const isStart = carousel.current.scrollLeft === 0;
const isEnd =
carousel.current.scrollLeft + carousel.current.offsetWidth === carousel.current.scrollWidth;
setIsAtStart(isStart);
setIsAtEnd(isEnd);
}
};
const handleClickNext = () => {
carousel.current?.scrollBy(300, 0);
updateButtonVisibility();
};
const handleClickPrev = () => {
carousel.current?.scrollBy(-300, 0);
updateButtonVisibility();
};
useEffect(() => {
updateButtonVisibility(); // Kollar startposition
const handleScroll = () => {
if (letterMenuRef.current) {
setIsSticky(window.scrollY >= letterMenuRef.current.offsetTop);
}
};
const handleCarouselScroll = () => {
updateButtonVisibility();
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
return (
<div
ref={letterMenuRef}
className={cn('sticky top-0 z-10 w-screen bg-white', {
'shadow-custom': isSticky,
})}
>
{!isAtStart && (
<button
className="prev space-between absolute left-0 top-1/2 -translate-y-1/2 cursor-pointer content-center rounded-full px-2"
onClick={handleClickPrev}
aria-label="vänster"
>
<div className="inline-flex items-center justify-center rounded-full border border-transparent bg-black bg-opacity-20 px-4 shadow-lg">
<NavigateIcon direction="left" variant="secondary" />
</div>
</button>
)}
{/* Vi har här valt att ha skräddarsydd inline-styling i CSS då det endast gäller denna komponenten */}
<ul
ref={carousel}
className="carousel flex justify-center gap-[0.624rem] overflow-x-auto scroll-smooth py-4"
>
{Letters.filter((letter) => activeLetters.includes(letter)).map((letter, letterIndex) => (
<li key={`nav_${letter}`}>
<Link
href={`#${letter}`}
onClick={handleClick}
className={`focus:ring-blue-500 rounded-full font-livvic text-xl font-bold uppercase underline focus:outline-none focus:ring-2 focus:ring-greenblue-primary sm:p-4 ${
activeLetters[currentElementIndexInViewport] === letter
? 'active bg-greenblue-1.45 p-2 no-underline'
: ''
}`}
style={{
width: '2.1rem',
height: '2.1rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
tabIndex={0}
>
{letter}
</Link>
</li>
))}
</ul>
{!isAtEnd && (
<button
className="next absolute right-0 top-1/2 -translate-y-1/2 cursor-pointer content-center justify-center rounded-full px-4 shadow"
onClick={handleClickNext}
aria-label="höger"
>
<NavigateIcon direction="right" variant="secondary" />
</button>
)}
</div>
);
};
export default LetterMenu;
您面临的问题可能是由于可滚动列表的初始滚动位置造成的。当组件安装时,它可能不会一直滚动到开始处。
为了确保列表从第一个字母开始,您可以在组件安装时以编程方式将列表滚动到开头。您可以通过在组件安装上运行的 useEffect 挂钩中添加一行代码来完成此操作。
试试这个:
useEffect(() => {
// Scroll to start when component mounts
if (carousel.current) {
carousel.current.scrollLeft = 0;
}
updateButtonVisibility(); // Check start position
const handleScroll = () => {
if (letterMenuRef.current) {
setIsSticky(window.scrollY >= letterMenuRef.current.offsetTop);
}
};
const handleCarouselScroll = () => {
updateButtonVisibility();
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);```