Tailwind移动端字母导航栏滚动异常:无法显示并滚动至A
问题:移动端字母导航菜单默认从J开始,无法滚动到A
我开发了一个基于React的Next.js应用,采用Tailwind CSS构建。页面组件展示按瑞典字母表A到Ö排序的人员列表,移动端交互要求字母列表单行显示,支持箭头或手指滑动滚动。
问题现象:在移动端视图下,字母列表默认从字母J开始显示而非A,推测是内容溢出屏幕导致,用户无法滚动到字母A。
需求:让字母列表可完整滚动至起始字母A,或直接默认从A开始显示。
解决方案
1. 强制初始滚动到最左侧
在组件挂载时,将滚动容器的scrollLeft设为0,确保默认从第一个字母(A)开始显示。
2. 修复容器对齐方式
将字母列表容器的justify-center改为justify-start,避免内容溢出时默认居中显示,让列表从左侧起始位置排列。
3. 优化状态更新时机
在activeLetters变化时重新检测滚动状态并重置位置,避免过滤后的字母列表变化导致的位置异常。
修改后的LetterMenu组件代码
'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(() => { // 强制滚动到最左侧,默认显示A if (carousel.current) { carousel.current.scrollLeft = 0; } updateButtonVisibility(); const handleScroll = () => { if (letterMenuRef.current) { setIsSticky(window.scrollY >= letterMenuRef.current.offsetTop); } }; const handleCarouselScroll = () => { updateButtonVisibility(); }; window.addEventListener('scroll', handleScroll); carousel.current?.addEventListener('scroll', handleCarouselScroll); return () => { window.removeEventListener('scroll', handleScroll); carousel.current?.removeEventListener('scroll', handleCarouselScroll); }; }, [activeLetters]); // 当activeLetters变化时重新执行 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-start 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;
内容的提问来源于stack exchange,提问作者Christer Johansson
相关产品推荐
相关产品推荐

