簡體   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