概要
タイトルを読んだだけでは、どのような記事か想像しづらいと思うので、最初に概要を説明します。
条件
同じような内容の要素を繰り返し処理で複数件表示している場合
画像では名前だけが異なる「フォルダボタン」をmapで複数件表示しています。
対応内容
複数件表示している各要素の「メニューボタン(・が縦に3つ並んでいるボタン)」を押下した際、各要素の真横にメニューを表示する。
例1:買い物メモの「メニューボタン」を押下
例2:やりたいことリストの「メニューボタン」を押下
手順
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に座標を格納するという発想し、それを基に描画するという発想は自分にはありませんでした。
記事にしたことで、今後の対応の引き出しとして蓄えられたら嬉しいです。