JavaScript自动完成组件键盘导航中scrollIntoView底部滚动异常问题
Autocomplete组件键盘导航滚动异常修复
问题根源
你的实现存在两个核心问题:
- 滚动位置计算逻辑错误,导致向下滚动时无法正确定位高亮项到列表底部
- 状态更新是异步的,直接在按键事件中调用滚动函数会使用旧的
highlightedIndex值
修复方案
1. 修正滚动计算函数
替换原scrollOptionIntoView函数,使用正确的位置计算逻辑:
const scrollOptionIntoView = () => { if (listRef.current && highlightedIndex >= 0) { const optionElement = listRef.current.children[highlightedIndex] as HTMLElement; if (!optionElement) return; const listContainer = listRef.current; const optionTop = optionElement.offsetTop; const optionBottom = optionTop + optionElement.offsetHeight; const viewportTop = listContainer.scrollTop; const viewportBottom = viewportTop + listContainer.clientHeight; // 向上滚动:高亮项顶部超出视口时,滚动到视口顶部对齐 if (optionTop < viewportTop) { listContainer.scrollTop = optionTop; } // 向下滚动:高亮项底部超出视口时,滚动到视口底部对齐 else if (optionBottom > viewportBottom) { listContainer.scrollTop = optionBottom - listContainer.clientHeight; } } };
2. 解决异步状态更新问题
移除按键事件中直接调用scrollOptionIntoView的代码,改用useEffect监听highlightedIndex变化后执行滚动:
修改handleKeyDown:
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => { if (event.key === "ArrowUp") { event.preventDefault(); setHighlightedIndex((prevIndex) => (prevIndex > 0 ? prevIndex - 1 : prevIndex)); } else if (event.key === "ArrowDown") { event.preventDefault(); setHighlightedIndex((prevIndex) => prevIndex < options.length - 1 ? prevIndex + 1 : prevIndex ); } else if ( event.key === "Enter" && highlightedIndex >= 0 && highlightedIndex < options.length ) { event.preventDefault(); setInputValue(options[highlightedIndex]); setHighlightedIndex(-1); } };
添加useEffect监听:
import { useEffect } from 'react'; // ... 其他组件代码 useEffect(() => { // 仅在下拉框展开且有高亮项时执行滚动 if (open && highlightedIndex >= 0) { scrollOptionIntoView(); } }, [highlightedIndex, open]);
修复说明
- 滚动逻辑修正:原代码中错误地在
optionBottom计算中加入了scrollTop,导致底部位置计算失真;同时optionTop的偏移计算不符合顶部对齐需求,修正后直接基于元素自身的offsetTop和offsetHeight与视口边界对比,确保滚动行为精准。 - 异步状态处理:React的
setState是异步操作,按键事件中直接调用滚动函数会读取到未更新的highlightedIndex,通过useEffect监听状态变化,确保滚动操作始终基于最新的高亮项索引执行。
内容的提问来源于stack exchange,提问作者Raihan Chowdhory
相关产品推荐
相关产品推荐

