Next.js模态框优化:仅外部滚动关闭,内部滚动不触发
解决模态框内部滚动触发关闭的问题
你的问题核心是window的scroll事件会在模态框内部滚动(尤其是滚轮滚动到底部/顶部后继续滚动)时被误触发,导致模态框意外关闭。下面提供两种可行的解决方案:
方案一:阻止内部容器的滚轮事件冒泡
这种方法直接阻止内部滚动的滚轮事件传递到window,避免误触发window的scroll监听,实现简单高效。
修改步骤
- 给内部滚动容器添加
onWheel事件处理函数,阻止事件冒泡 - 修正原
useEffect的依赖项(添加onClose,避免闭包导致的函数引用过时问题)
修改后的完整代码
import { useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { XMarkIcon } from '@heroicons/react/24/outline'; // 请替换为你的redux相关导入路径 import { selectModal, setModal, resetFunc } from '../your-redux-path'; function HomeFilterModal({ visible, onClose }) { const toggle = useSelector(selectModal); const dispatch = useDispatch(); const handleOnClose = (e) => { if (e.target.id === "container") onClose(); }; // 阻止内部容器的滚轮事件冒泡到window const handleInnerWheel = (e) => { e.stopPropagation(); }; useEffect(() => { const handleScroll = () => { if (window.scrollY >= 10) { onClose(); } }; window.addEventListener("scroll", handleScroll); return () => window.removeEventListener("scroll", handleScroll); }, [onClose]); // 添加onClose到依赖,修复闭包问题 if (!visible) return null; return ( <div id="container" onClick={handleOnClose} className="fixed top-24 bottom-0 left-0 right-0 bg-black bg-opacity-30 backdrop-blur-sm h-screen z-50 hidden sm:flex justify-center " > <div id="nonscroll" onWheel={handleInnerWheel} // 绑定滚轮事件处理 className=" h-[28rem] w-2/3 bg-[#fafaf9] drop-shadow-md rounded-3xl flex flex-col items-center overflow-hidden overflow-y-scroll scrollbar-none snap-y" > <div className="flex items-center justify-between w-full px-4 py-4 border-b"> <div onClick={() => dispatch(setModal(!toggle))} className=" rounded-full border h-8 w-8 border-gray-600 hover grid place-items-center hover:border-0 hover:drop-shadow-2xl hover:shadow-black" > <XMarkIcon className="h-4 w-4" /> </div> <p className="text-2xl tracking-wider">Filter</p> <p onClick={() => dispatch(resetFunc())} className="text-sm hover:underline pr-2 cursor-pointer" > Clear All </p> </div> <HomePriceFilter /> <HomeLocationFilter /> <HomeTypeFilter /> </div> </div> ); } export default HomeFilterModal;
方案二:标记内部滚动状态,跳过window滚动处理
这种方法通过状态标记当前是否在内部滚动,在window的scroll事件中跳过关闭逻辑,适合需要更精细控制的场景。
修改核心代码片段
import { useState, useEffect } from 'react'; // ...其他代码保持不变 const [isInnerScroll, setIsInnerScroll] = useState(false); // 防抖函数,避免频繁更新状态 const debounce = (func, delay = 100) => { let timer; return () => { clearTimeout(timer); timer = setTimeout(func, delay); }; }; useEffect(() => { const innerContainer = document.getElementById('nonscroll'); const handleInnerScroll = () => { setIsInnerScroll(true); debounce(() => setIsInnerScroll(false))(); }; innerContainer.addEventListener('scroll', handleInnerScroll); return () => innerContainer.removeEventListener('scroll', handleInnerScroll); }, []); useEffect(() => { const handleScroll = () => { // 仅当非内部滚动时触发关闭 if (!isInnerScroll && window.scrollY >= 10) { onClose(); } }; window.addEventListener("scroll", handleScroll); return () => window.removeEventListener("scroll", handleScroll); }, [isInnerScroll, onClose]);
额外优化建议
- 如果希望模态框打开时页面完全无法滚动,可以在模态框显示时给
body添加overflow-hidden样式,关闭时移除,从根源避免外部滚动的触发 - 原代码中
useEffect的空依赖数组会导致onClose引用过时,必须添加onClose到依赖数组,否则可能出现关闭逻辑失效的问题
内容的提问来源于stack exchange,提问作者Mohammed Bekele
相关产品推荐
相关产品推荐

