You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js模态框优化:仅外部滚动关闭,内部滚动不触发

解决模态框内部滚动触发关闭的问题

你的问题核心是window的scroll事件会在模态框内部滚动(尤其是滚轮滚动到底部/顶部后继续滚动)时被误触发,导致模态框意外关闭。下面提供两种可行的解决方案:

方案一:阻止内部容器的滚轮事件冒泡

这种方法直接阻止内部滚动的滚轮事件传递到window,避免误触发window的scroll监听,实现简单高效。

修改步骤

  1. 给内部滚动容器添加onWheel事件处理函数,阻止事件冒泡
  2. 修正原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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 00:55:33