AppRouterの@modalで背後のコンテンツのスクロールを制御する
2023-12-14
a year ago
開発環境
- next 13.5.4
前提
以前の記事で紹介したAppRouterでモーダルを実装するの続きとなります。
この記事ではモーダルの詳細の実装は省いていますのでご了承ください。
本題で説明するモーダルコンポーネントは以下になります。
'use client'
import { useCallback, useRef, useEffect, MouseEventHandler } from 'react'
import { useRouter } from 'next/navigation'
export default function Modal({ children }: { children: React.ReactNode }) {
const overlay = useRef(null)
const wrapper = useRef(null)
const router = useRouter()
const onDismiss = useCallback(() => {
router.back()
}, [router])
const onClick: MouseEventHandler = useCallback(
(e) => {
if (e.target === overlay.current || e.target === wrapper.current) {
if (onDismiss) onDismiss()
}
},
[onDismiss, overlay, wrapper]
)
const onKeyDown = useCallback(
(e: KeyboardEvent) => {
if (e.key === 'Escape') onDismiss()
},
[onDismiss]
)
useEffect(() => {
document.addEventListener('keydown', onKeyDown)
return () => document.removeEventListener('keydown', onKeyDown)
}, [onKeyDown])
return (
<div
ref={overlay}
className="fixed z-10 left-0 right-0 top-0 bottom-0 mx-auto bg-black/60"
onClick={onClick}
>
<div
ref={wrapper}
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full sm:w-10/12 md:w-8/12 lg:w-1/2 p-6"
>
{children}
</div>
</div>
)
}
本題
公式ドキュメントで紹介されているモーダルをそのまま利用すると、モーダルがマウントした状態で、body要素をスクロールできてしまいます。
これでも問題ないケースはあると思いますが、例えばモーダル自身がフルスクリーンの場合やモーダルの中でスクロール要素がある場合は不自然な動きだとユーザーに認識される可能性があります。
この場合、モーダルコンポーネントに以下の処理を追加するだけで回避できます。
useEffect(() => {
document.body.style.overflow = 'hidden';
return () => {
document.body.style.overflow = 'auto';
};
}, []);
さいごに
他にも方法があれば教えてください!