繁体   English   中英

如何使用箭头键遍历 React 列表中的项目列表

[英]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 之外不会做任何事情
  • 当您更新活动键时,您不会专注于活动元素

资源:

沙盒代码: 编辑confidence-sinoussi-rlzdrf

演示: 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM