JavaScript - React ドロップダウンの onClick でのアクセシビリティ

okwaves2024-01-25  6

現時点では、このコードは機能しますが、ユーザーがクリックしてメニューを非表示にすると、useClickOutside も起動し、メニューがオフになったりオンになったりします...外側をクリックすると閉じるように修正する方法はありますか?しかし、ボタンをクリックするとオン/オフが切り替わりますか?

const useClickOutside = (ref, handler) => {
    useEffect(() => {
        const clickHandler = (event) => {
            if (!ref.current || ref.current.contains(event.target)) {
                return;
            }
            handler(event);
        };

        document.addEventListener('mousedown', clickHandler);

        return () => {
            document.removeEventListener('mousedown', clickHandler);
        };
    });
};

const Settings = () => {
    const ref = useRef();
    const [toggle, setToggle] = useState(false);
    useClickOutside(ref, () => setToggle(false));

    return (
        <div className='settings'>
            <button onClick={() => setToggle(!toggle)} className='settings__button'>
                Menu
            </button>
            {toggle && (
                <div ref={ref} className='settings__panel'>
                    <Link className='settings__links' to='/user/settings'>
                        Your Profile
                    </Link>
                    <Link className='settings__links' to='/user/settings'>
                        Todos history
                    </Link>
                    <Link className='settings__links' to='/user/settings'>
                        Settings
                    </Link>
                    <Link className='settings__links' value={'Logout'} to='/user/login'>
                        Logout
                    </Link>
                </div>
            )}
        </div>
    );
};


------------------------

tabIndex=0 を指定して .settings div に onBlur イベントを追加することを検討してください。

その後、div のブラーをキャプチャして、イベントが div 内から来たかどうかをテストできます。

const onBlur = (e: FocusEvent < HTMLElement > ) => {
  if (opened?) {
    const element = e.relatedTarget;

    if (element == null) {
      // dropdown was blured because window lost focused. probably close.
    } else if (element != e.currentTarget) {
      if (!e.currentTarget.contains(element as Node)) {
        // blured element is not in .settings. close
      }
    }
  }
};

さらに凝りたい場合は、キーダウンを追加してエスケープ時に閉じることもできます。

const onKeyDown = (e: KeyboardEvent) => {
  if (e.key === "Escape") {
    // close!
  }
);

これは、実装を含むコードサンドボックスです。これらのアイテムを要素化します。

6

それを理解するのは少し難しいですが、このヒントに取り組んでみてください。タイ。

– 西

2020 年 9 月 5 日 1:44

何を明確にする必要がありますか?

– ブレンデン

2020 年 9 月 5 日 1:48

1

それをすべて削除してください。それはタイプスクリプトからのものです。既存のコードサンプルがありました。また、提案した変更のサンボックスを含めるように投稿を更新しました。

– ブレンデン

2020 年 9 月 5 日 2:14

1

onBlur/onFocus の存在を強調していただきありがとうございます。これによりコードが大幅に簡素化されました。私はもう少し簡単に書きました。なぜアルが必要なのですかl if/ifelse? codesandbox.io/s/dropdown-showcase-iokzz テスト目的で多くのネスト要素を設定しました。

– 西

2020 年 9 月 5 日 2:57

1

要素 == null は、ドームの外側で発生するブラー用です。ユーザーが別のタブまたはウィンドウをクリックしたとします。 elements.contains は、ドロップダウン内でぼかしが発生する可能性があるためです。たとえば、ドロップダウンに検索ボックスやチェックボックスなどがあるとします。ユーザーがそこに注目してクリックすると、ブラーがトリガーされ、ブラーが内部から発生したかどうかを知る必要があります。

&ndアッシュ ブレンデン

2020 年 9 月 5 日 3:07



------------------------

イベント stopPropagation を利用できます。メニューを非表示にする onClick ハンドラー関数に、event.stopPropagation() の呼び出しを追加します。

<button 
  onClick={(e) => {
    e.stopPropagation();
    setToggle(!toggle);
  }}
  className='settings__button'
  >
   Menu
</button>

これにより、onClick イベントが次のイベント リスナー (onClickOutside リスナー) に上向きに送信されるのを防ぎます。

更新:

これは、イベント リスナーが onClick イベントをリッスンしている場合にのみ機能します。インライン onClick イベント リスナーは、クリックのみのタイプのイベントの伝播を停止します。

document.addEventListener('click', clickHandler);

return () => {
  document.removeEventListener('click', clickHandler);
};

3

役に立ちません:/ ボタンがオン/オフを 2 回繰り返します。

– 西

2020 年 9 月 5 日 1:38

1

ああ、申し訳ありません。イベントリスナーはクリックイベントではなくマウスダウンをリッスンしているため、機能しませんでした。クリック イベントのリッスンに切り替えると、機能します。また、これは問題を解決する非常に一般的な方法です。たとえば、ここで同じ解決策を参照してください: stackoverflow.com/questions/51441562/…

– アンドレ

2020 年 9 月 5 日 21:24

ありがとうアンドレ。これは、私のこのカスタムフックの将来の目的に役立つでしょう。

– 西

2020 年 9 月 5 日 23:26

総合生活情報サイト - OKWAVES
総合生活情報サイト - OKWAVES
生活総合情報サイトokwaves(オールアバウト)。その道のプロ(専門家)が、日常生活をより豊かに快適にするノウハウから業界の最新動向、読み物コラムまで、多彩なコンテンツを発信。