前端如何解决滚动穿透问题详解

 更新时间:2025年03月11日 10:46:53   作者:IT枫斗者  
这篇文章主要介绍了前端解决滚动穿透问题的五种常见方法,并详细分析了它们的优缺点和适用场景,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

滚动穿透是指在移动端(或具有滚动条的容器中),当一个可滚动的模态框(或类似元素)被打开时,如果用户在该模态框内滚动到尽头,会导致底层页面(或父容器)也跟着滚动。这是一种糟糕的用户体验。

以下是前端解决滚动穿透问题的几种常见方法,以及它们的优缺点和适用场景:

1. overflow: hidden; (最简单,但有局限性)

  • 原理: 当模态框打开时,给 body 或根元素添加 overflow: hidden; 样式,阻止其滚动。模态框关闭时,移除该样式。

  • 实现 (以 Vue 为例):

    <template>
      <div>
        <button @click="showModal = true">Open Modal</button>
        <div v-if="showModal" class="modal">
          <div class="modal-content">
            <!-- 模态框内容 -->
            <button @click="showModal = false">Close</button>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          showModal: false,
        };
      },
      watch: {
        showModal(newValue) {
          if (newValue) {
            document.body.style.overflow = 'hidden';
          } else {
            document.body.style.overflow = ''; // 或 'auto'
          }
        },
      },
      beforeDestroy() { // 重要: 组件销毁时也要移除
        document.body.style.overflow = '';
      }
    };
    </script>
    
    <style>
    .modal {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background-color: rgba(0, 0, 0, 0.5);
      overflow-y: auto; /* 允许模态框自身滚动 */
    }
    .modal-content{
       /* 模态框内容样式 */
    }
    </style>
    
  • 优点: 简单易用,代码量少。

  • 缺点:

    • 页面跳动: 当 body 的滚动条被隐藏时,页面可能会因为滚动条消失而产生轻微的跳动(尤其是在 Windows 上)。
    • 丢失滚动位置: 关闭模态框后,body 的滚动位置会丢失,回到顶部。
    • 影响其他 fixed 元素: 如果页面上有其他 position: fixed 的元素(例如固定头部、固定侧边栏),它们的位置可能会受到影响,因为 fixed 定位是相对于视口的,而 body 的 overflow: hidden 可能会改变视口的计算方式。
    • 键盘可访问性问题: 如果模态框是使用键盘导航打开的,隐藏body的滚动可能会导致焦点丢失或行为异常.
  • 适用场景: 简单的模态框,不需要保留底层页面滚动位置,且页面上没有其他复杂的 fixed 元素。

2. 阻止事件冒泡 (适用于特定场景)

  • 原理: 在模态框的滚动容器上阻止 touchmove 事件的冒泡(以及可能的 wheel 事件)。

  • 实现:

    <template>
      <div v-if="showModal" class="modal">
        <div class="modal-content" @touchmove.prevent.stop @wheel.prevent.stop>
          </div>
      </div>
    </template>
    

    或使用原生JS

    javascript 代码解读复制代码 modalContent.addEventListener('touchmove', function(event) {
        event.preventDefault();
        event.stopPropagation(); //有时候不需要.stop,只阻止默认行为
     }, { passive: false }); //passive:false 是关键
    
* **注意**: 必须要加上 `{ passive: false }` , 默认 passive 是 true,表示不会调用 preventDefault(). 如果你设置了 passive: true, 又调用了 preventDefault(), 浏览器会忽略 preventDefault(), 并报一个警告。
  • 优点: 只阻止模态框内的滚动事件,不影响其他元素。
  • 缺点:
    • 只能阻止 touchmove 和 wheel: 无法阻止通过键盘(如 Page Up/Down)或滚动条拖动引起的滚动。
    • 模态框内部滚动问题: 如果模态框内部本身有多个可滚动区域,阻止 touchmove 会导致这些区域也无法滚动。 需要更精细的控制,例如判断当前滚动元素是否已经到达边界。
  • 适用场景: 模态框内部只有一个滚动区域,并且不需要键盘或滚动条进行滚动的情况。

3. position: fixed; (较好的解决方案)

  • 原理: 当模态框打开时,将 body 设置为 position: fixed;,并记录当前的滚动位置(scrollTop)。关闭模态框时,恢复 body 的定位,并设置回之前记录的滚动位置。

  • 实现:

    <template>
      <div v-if="showModal" class="modal" @touchmove.prevent>  </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          showModal: false,
          scrollTop: 0,
        };
      },
      watch: {
        showModal(newValue) {
          if (newValue) {
            this.scrollTop = window.pageYOffset || document.documentElement.scrollTop;
            document.body.style.position = 'fixed';
            document.body.style.top = `-${this.scrollTop}px`;
            document.body.style.width = '100%'; // 确保 body 宽度占满
          } else {
            document.body.style.position = '';
            document.body.style.top = '';
            document.body.style.width = '';
            window.scrollTo(0, this.scrollTop);
          }
        },
      },
       beforeDestroy() { // 重要: 组件销毁时也要移除
         document.body.style.position = '';
            document.body.style.top = '';
            document.body.style.width = '';
            window.scrollTo(0, this.scrollTop);
       }
    };
    </script>
    
  • 优点:

    • 保留滚动位置: 关闭模态框后,body 的滚动位置可以恢复。
    • 避免页面跳动: position: fixed; 不会隐藏滚动条,避免了跳动问题。
    • 相对较好地兼容 fixed 元素: 虽然fixed会脱离文档流,但是因为设置了top属性为负的滚动高度,整体页面视觉上不会移动.
  • 缺点:

    • 代码略复杂: 需要记录和恢复滚动位置。
    • 兼容性问题: 在一些旧版本的 iOS Safari 中,position: fixed; 可能会导致一些布局问题(虽然现代浏览器基本已修复)。
    • 需要处理滚动条: 需要处理可能存在的滚动条,例如设置bodywidth:100%, 以防止出现水平滚动条。
  • 适用场景: 大多数情况下的模态框,需要保留底层页面滚动位置,且对兼容性要求不高。

4. 使用第三方库

  • 原理: 一些第三方库(如 body-scroll-lock)封装了更完善的滚动锁定逻辑,处理了各种边界情况和兼容性问题。

  • 实现 (以 body-scroll-lock 为例):

    npm install body-scroll-lock
    
    <template>
      <div v-if="showModal" ref="modal" class="modal">
        <div class="modal-content">
           </div>
      </div>
    </template>
    
    <script>
    import { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock';
    
    export default {
      data() {
        return {
          showModal: false,
        };
      },
      watch: {
        showModal(newValue) {
          if (newValue) {
            disableBodyScroll(this.$refs.modal);
          } else {
            enableBodyScroll(this.$refs.modal);
          }
        },
      },
      beforeDestroy() {
        clearAllBodyScrollLocks(); // 清除所有锁定
      },
    };
    </script>
    
  • 优点:

    • 完善的解决方案: 处理了各种细节和兼容性问题。
    • 易于使用: API 简单明了。
  • 缺点: 需要引入额外的库,增加项目体积。

  • 适用场景: 对滚动锁定有较高要求,需要处理各种复杂情况,且不介意引入第三方库的项目。

5. 使用overscroll-behavior (CSS 属性,较新)

***原理**: `overscroll-behavior` CSS 属性控制当滚动到达元素边界时发生的情况。  它可以防止滚动链(即滚动穿透)。
* **实现**:
    ```css
    .modal-content {
       overscroll-behavior: contain; /* 或 overscroll-behavior-y: contain; */
       /* 其他样式 */
    }
    ```

* **优点**:
    * **原生 CSS 解决方案**: 无需 JavaScript。
    * **性能好**: 浏览器原生支持,性能通常优于 JavaScript 解决方案。
* **缺点**:
    * **兼容性**: 比较新的属性,一些旧浏览器可能不支持(需要检查 Can I Use:[https://caniuse.com/?search=overscroll-behavior](https://caniuse.com/?search=overscroll-behavior))。 可以考虑回退方案。
* **适用场景**:  如果项目对浏览器兼容性要求不高,`overscroll-behavior` 是一个非常优雅的解决方案。

总结和选择建议:

  • 简单场景,不需保留滚动位置: overflow: hidden;
  • 模态框内只有一个滚动区域,且不需要键盘/滚动条滚动: 阻止事件冒泡
  • 大多数情况,需要保留滚动位置: position: fixed;
  • 需要处理复杂情况,不介意引入库: body-scroll-lock
  • 浏览器兼容性允许: overscroll-behavior (首选,最优雅)

最佳实践是根据项目的具体需求和目标浏览器来选择最合适的方法。通常,position: fixed; 或 body-scroll-lock 是比较稳妥的选择。如果对兼容性要求不高,强烈推荐 overscroll-behavior。 记得在开发过程中进行充分的测试,确保在各种设备和浏览器上都能正常工作。

总结

到此这篇关于前端如何解决滚动穿透问题的文章就介绍到这了,更多相关前端滚动穿透问题内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JavaScript优化图片懒加载的性能技巧

    JavaScript优化图片懒加载的性能技巧

    前端发展过程中有许多性能优化的操作,比如防抖、节流和图片懒加载等,在这里我们首先聊聊图片懒加载操作,我们会经常逛像淘宝和京东等购物平台,一次性全部加载会导致加载时间长、网络资源消耗大,所以本文给大家介绍了JavaScript优化图片懒加载的性能技巧
    2024-06-06
  • JavaScript对象之深度克隆介绍

    JavaScript对象之深度克隆介绍

    这篇文章主要介绍了JavaScript对象之深度克隆介绍,本文详细的讲解了什么是对象深度克隆,并给出了示例代码,需要的朋友可以参考下
    2014-12-12
  • JS 非图片动态loading效果实现代码

    JS 非图片动态loading效果实现代码

    功能说明:譬如在按某个button时,显示消息"Loading”,然后每隔一秒后后面加上".",至一定数量的"."时如:"Loading...",再重置此消息为"Loading",继续动态显示,直至按钮事件处理完成。
    2010-04-04
  • JavaScript实现简单的音乐播放器

    JavaScript实现简单的音乐播放器

    这篇文章主要为大家详细介绍了JavaScript实现简单的音乐播放器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • 详谈表单重复提交的三种情况及解决方法

    详谈表单重复提交的三种情况及解决方法

    下面小编就为大家带来一篇详谈表单重复提交的三种情况及解决方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • 阻止JavaScript事件冒泡传递(cancelBubble 、stopPropagation)

    阻止JavaScript事件冒泡传递(cancelBubble 、stopPropagation)

    阻止JavaScript事件冒泡传递(cancelBubble 、stopPropagation)...
    2007-05-05
  • 微信小程序接入微信支付实现过程详解

    微信小程序接入微信支付实现过程详解

    这篇文章主要介绍了微信小程序接入微信支付实现过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-12-12
  • JS传递对象数组为参数给后端,后端获取的实例代码

    JS传递对象数组为参数给后端,后端获取的实例代码

    下面小编就为大家带来一篇JS传递对象数组为参数给后端,后端获取的实例代码。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • webpack2.0搭建前端项目的教程详解

    webpack2.0搭建前端项目的教程详解

    这篇文章主要给大家介绍了关于webpack2.0搭建前端项目的相关资料,文中介绍的非常详细,对大家学习或者使用webpack2.0具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-04-04
  • document.getElementById的一些细节

    document.getElementById的一些细节

    document.getElementById的一些细节...
    2006-09-09

最新评论