3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Reactでドロップダウン(dropdown)メニューを実装する

Posted at

経緯

Reactでドロップダウンメニューを実装する必要が出てきた。
ドロップダウンメニューを実装するには、領域外のクリックを検知する必要がある。
React上で領域外を検知する方法と、簡単にドロップダウンメニューを作る。

必要な知識

React React hooks

Code


import { useState, useRef, useEffect } from 'react'
const Menu = () => {
    const [isOpen, setIsOpen] = useState(false);
    const dropdownRef = useRef();

    useEffect(() => {
        document.addEventListener("mousedown", handleOutsideClick);
        return () => document.removeEventListener("mousedown", handleOutsideClick);
    }, []);

    const handleOutsideClick = (e) => {
        if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
            setIsOpen(false);
        }
    };

    return(
        <>
            <div ref={dropdownRef} className="relative inline-block text-left">
                <span className="rounded-md shadow-sm">
                    <button onClick={() => setIsOpen(!isOpen)} type="button" className="inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" id="options-menu" aria-haspopup="true" aria-expanded={isOpen}>
                    MENU
                    <svg className="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#4B5563">
                        <path fillRule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clipRule="evenodd" />
                    </svg>
                    </button>
                </span>
        
                {isOpen && (
                    <div className='absolute top-12 right-2 w-64 p-4 bg-white z-50 shadow-lg border border-gray-100 rounded'>
                        <ul>
                            <li><a>menu1</a></li>
                            <li><a>menu2</a></li>
                            <li><a>menu3</a></li>
                        </ul>
                    </div>
                )}
            </div>
        </>
    )
}

export default Menu

仕組み

useEffectで描画時に、イベントリスナーでmousedown(クリック)を登録しておく。
また、returnでイベントリスナーのクリーンアップも設定する。

mousedown時に、mousedownされた要素が返されるため、handleOutsideClickでその要素に、ドロップダウンメニューの要素が含まれるか判定する。(ドロップダウンメニューの要素はuseRefを使用して取得)
含まれる場合、それはドロップダウンメニュー上でのクリックなので、ドロップダウンメニューは表示し続ける。
含まれない場合、それはドロップダウンメニュー外でのクリックのためドロップダウンメニューを非表示にする。

今回は、要素を丸ごと消しているが、display: hidden;display: none;でも実装可能である。

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?