1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React + TypeScript】メニューボタンの押下位置に応じたメニューの表示

Last updated at Posted at 2025-04-27

概要

タイトルを読んだだけでは、どのような記事か想像しづらいと思うので、最初に概要を説明します。

条件

同じような内容の要素を繰り返し処理で複数件表示している場合

画像では名前だけが異なる「フォルダボタン」をmapで複数件表示しています。

フォルダ一覧.png

対応内容

複数件表示している各要素の「メニューボタン(・が縦に3つ並んでいるボタン)」を押下した際、各要素の真横にメニューを表示する。

例1:買い物メモの「メニューボタン」を押下

買い物メモメニュー.png

例2:やりたいことリストの「メニューボタン」を押下

やりたいことリストメニュー.png

手順

1. 各「メニューボタン」押下時に、ボタンを一意に識別できる情報(ここではfolder._id)とイベント情報を引数に関数を実行する。

// 「フォルダボタン」「メニューボタン」を1つの組として表示。
{folders.map((folder) => (
    <div key={folder._id} className="folder-container">
      <button
        className="Folder-button"
        onClick={() => setSelectedFolder(folder)}
      >
        {/**省略**/}
      </button>
      {/**メニューボタン**/}
      <button
        className="folderMenu-button"
        onClick={(event) => toggleMenu(folder, event)} //実行する関数
      >
        {/**省略**/}
      </button>
    </div>
  ))}

2. 項番1の引数をそれぞれstateに保存する。

  // メニューの開閉
  const [openMenu, setOpenMenu] = useState<boolean>(false);
  // メニューボタンの位置を格納する
  const [menuPosition, setMenuPosition] = useState<{
    top: number;
    left: number;
  }>({ top: 0, left: 0 });

  // メニューボックスを開く
  const toggleMenu = (
    folder: FolderInterface,
    event: React.MouseEvent<HTMLButtonElement>
  ) => {
    // DOMRectオブジェクトからクリックされた要素の座標を受け取り、stateに保存する。
    const rect = event.currentTarget.getBoundingClientRect();
    setMenuPosition({
      top: rect.top + window.scrollY - 152,
      left: rect.right + window.scrollX,
    });
    setOpenMenu(true);
  };

DOMRectオブジェクトとは、要素の位置やサイズ情報を保持するオブジェクトのこと

-152をしている理由については後述する。

3. 項番2でstateに保存した値を基にメニューを表示する。

<div
  className="FolderCD-container"
  style={{
    position: 'absolute',
    top: `${menuPosition.top}px`,
    left: `${menuPosition.left}px`,
  }}
>
    {/**名前を変更ボタン・削除ボタンを以下に記述しているが、省略**/}

注1: 以上を表示する条件を、項番2のsetOpenMenuIdでstateに保存された値があることとしている。(記事では省略)

注2: 項番2注2で-152をしている理由は、DOMRectオブジェクトから取得した座標は画面全体から見たときの位置であり、上記JSXは親要素を始点として描画されている。そのため、-152で補正していない場合ではメニューの描画に親要素から画面上端分のズレが生じていた。

所感

stateに座標を格納するという発想し、それを基に描画するという発想は自分にはありませんでした。
記事にしたことで、今後の対応の引き出しとして蓄えられたら嬉しいです。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?