[英]How to iterate through list of items in React list using arrow keys
我在这个问题上看到过类似的帖子,但是当我尝试实施他们的建议时,它永远不会奏效。 我有活动状态和我的搜索结果,它们在调度时返回。 当我输入输入搜索或单击它时,菜单会在其下方打开并显示结果,但是当我按下箭头键时,它会停留在输入栏中并且不会遍历/聚焦每个列表项。 我想这样做,以便当我点击向下或向上箭头键时,它会遍历搜索结果,我可以在其中任何一个上按 Enter 以触发搜索。
const [active, setActive] = useState(0)
const searchResults = useSelector((state) => Object.values(state.searchReducer.Search))
const keyDown = (e) => {
if(e.key === 38 && searchResults.length > 0){
setActive(active - 1)
}else if(e.key === 40 && active < searchResults.length - 1){
setActive(active + 1)
}
}
<BsSearch className="search-icon" />
<form onSubmit={handleSubmit}>
<input
onClick={() => setMenu(true)}
className="search-input"
autoFocus
onKeyDown={keyDown}
type="search"
placeholder="Search"
onChange={(e) => setSearchVal(e.target.value)}
value={searchVal}
/>
</form>
{menu && (
<div className="search-cont">
{searchResults.length > 0 ? (
searchResults?.map((ele, i) => {
return (
<NavLink onClick={(() => {setSearchVal('')})} key={i} className="spot-
links" to={`/users/${ele.id}`}>
<div
className="search-results-list"
id={active === i ? 'active' : null}
>
{ele.username}
</div>
</NavLink>
);
})
```
使用您当前的方法,有几个因素对您不利:
id
设置为“活动”,除了更改元素的 id 之外不会做任何事情资源:
演示: https ://rlzdrf.csb.app/
带注释的代码(为简单起见,我使用带有简单按钮交互的硬编码搜索数据):
import { useCallback, useState, useEffect } from "react";
import Instructions from "./Instructions";
import SEARCH_DATA from "./searchData";
export default function App() {
const [activeIndex, setActiveIndex] = useState(0);
const [showResults, setShowResults] = useState(false);
const handleClick = (name: string) => {
alert(`You've selected ${name}!`);
};
// captures up/down/enter/escape keys
const handleSelection = useCallback(
(event: KeyboardEvent) => {
// prevent key presses from being captured if the results are hidden
if (!showResults) return;
const { key } = event;
const arrowUpPressed = key === "ArrowUp";
const arrowDownPressed = key === "ArrowDown";
const escapePressed = key === "Escape" || key === "Esc";
const enterPressed = key === "Enter";
const searchDataLength = SEARCH_DATA.length - 1;
// increments active index (wraps around when at top)
if (arrowUpPressed) {
event.preventDefault();
setActiveIndex((currentIndex) =>
currentIndex - 1 >= 0 ? currentIndex - 1 : searchDataLength
);
// decrements active index (wraps around when at bottom)
} else if (arrowDownPressed) {
event.preventDefault();
setActiveIndex((currentIndex) =>
currentIndex + 1 <= searchDataLength ? currentIndex + 1 : 0
);
// alerts active selection
} else if (enterPressed) {
event.preventDefault();
const target = event.target as HTMLElement;
alert(`You've selected ${target.dataset.name}!`);
// closes results
} else if (escapePressed) {
event.preventDefault();
setActiveIndex(0);
setShowResults(false);
}
},
[showResults]
);
// globally listens for key presses
useEffect(() => {
document.addEventListener("keydown", handleSelection);
return () => {
document.removeEventListener("keydown", handleSelection);
};
}, [handleSelection]);
// when the active selection changes, it focuses on the active element
useEffect(() => {
if (showResults) {
const activeElement = document.querySelector(
`[data-index='${activeIndex}']`
) as HTMLElement;
if (activeElement) activeElement.focus();
}
}, [showResults, activeIndex]);
return (
<div className="app">
<h1>Hello CodeSandbox</h1>
<h2>Press the "Show Results" button!</h2>
<Instructions />
<button
className="results"
type="button"
onClick={() => {
// toggle results
setShowResults((r) => !r);
// reset active index to 0 when results are closed
if (!showResults) setActiveIndex(0);
}}
>
Show Results
</button>
{showResults && SEARCH_DATA.length > 0
? SEARCH_DATA.map(({ id, name }, index) => (
<button
type="button"
role="presentation"
data-index={index}
data-name={name}
className={`name ${activeIndex === index ? "active" : ""}`}
key={id}
onClick={() => {
setActiveIndex(index);
handleClick(name);
}}
>
{name}
</button>
))
: null}
</div>
);
}
这只是完成上述任务的众多方法之一,因此请随意探索其他方法。 例如,您可能希望将焦点吸引到搜索结果中并聚焦键。 此外,如果您正在为可访问性进行设计,最好对WCAG 2.1标准进行一些研究——它们还为各种不同的用例提供了一些很好的示例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.