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

弹窗显示时禁止页面滚动(兼容iOS移动端,弹窗可滚动)

iOS 移动端弹窗禁止页面滚动+弹窗可滚动的靠谱方案

问题根源

iOS Safari的弹性滚动机制很特殊:当弹窗滚动到顶部/底部时,继续滑动会触发底层页面的滚动。常规的overflow: hidden、第三方框架之所以失效,要么是没针对iOS的弹性滚动做处理,要么是操作body时没处理滚动位置导致黑条。

直接上可运行的方案

第一步:先写好CSS

给弹窗容器加滚动样式,同时给body准备一个锁定类:

/* 弹窗容器:确保自身可滚动,且iOS下顺滑 */
.modal-container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch; /* 必须加,开启iOS原生顺滑滚动 */
  background: rgba(0,0,0,0.5);
  padding: 20px;
  box-sizing: border-box;
}

/* 弹窗内容:避免容器滚动时内容太贴边 */
.modal-content {
  background: white;
  padding: 15px;
  border-radius: 8px;
}

/* 弹窗打开时给body加的类:固定位置+禁止滚动 */
body.modal-open {
  position: fixed;
  width: 100%;
  overflow: hidden;
}

第二步:React 中的实现逻辑(解决黑条+彻底锁滚动)

import { useEffect, useState } from 'react';

const Modal = ({ isOpen, onClose, children }) => {
  // 存弹窗打开前的滚动位置,用来关闭时恢复
  const [prevScrollTop, setPrevScrollTop] = useState(0);

  useEffect(() => {
    if (!isOpen) return;

    // 1. 记录当前滚动位置
    const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
    setPrevScrollTop(scrollTop);

    // 2. 锁定body滚动:加类+设置top偏移,避免页面跳回顶部
    document.body.classList.add('modal-open');
    document.body.style.top = `-${scrollTop}px`;

    // 3. 监听弹窗的touchmove事件,阻止边界滚动时触发页面滚动
    const handleModalTouchMove = (e) => {
      const modal = e.currentTarget;
      // 判断弹窗是否滚到顶/底
      const isTop = modal.scrollTop === 0;
      const isBottom = modal.scrollHeight - modal.scrollTop === modal.clientHeight;

      // 如果在顶部还往下滑,或者在底部还往上滑,就阻止默认行为
      if ((isTop && e.touches[0].clientY > e.changedTouches[0].clientY) ||
          (isBottom && e.touches[0].clientY < e.changedTouches[0].clientY)) {
        e.preventDefault();
      }
    };

    const modalEl = document.querySelector('.modal-container');
    if (modalEl) {
      // passive: false 必须加,不然preventDefault可能无效
      modalEl.addEventListener('touchmove', handleModalTouchMove, { passive: false });
    }

    // 清理函数:关闭弹窗时恢复
    return () => {
      document.body.classList.remove('modal-open');
      document.body.style.top = '';
      // 滚动回之前的位置,彻底解决黑条问题
      window.scrollTo(0, prevScrollTop);

      if (modalEl) {
        modalEl.removeEventListener('touchmove', handleModalTouchMove);
      }
    };
  }, [isOpen]);

  if (!isOpen) return null;

  return (
    <div className="modal-container">
      <div className="modal-content">
        {children}
        <button onClick={onClose} style={{ marginTop: '20px' }}>关闭弹窗</button>
      </div>
    </div>
  );
};

export default Modal;

为啥之前的方法无效?

  • overflow: hidden:iOS Safari 对body设置这个属性时,弹性滚动触发后还是会带动页面动,根本拦不住。
  • touchAction: none:全局设置会把弹窗自身的滚动也禁了,直接废了弹窗的滚动功能。
  • body-scroll-lock:你没传弹窗元素作为目标(正确用法是disableBodyScroll(modalEl)),所以它不知道要保护哪个元素的滚动,自然无效。
  • 那个CSS方案:关闭弹窗时没把页面滚回原来的位置,导致页面位置偏移,出现黑条。

内容的提问来源于stack exchange,提问作者János

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 06:15:35