模态框打开时固定页面滚动且保留滚动条的无JS方案
解决方案:纯CSS锁定页面滚动,保留滚动条占位且不跳转顶部
核心思路
通过CSS的:has()选择器监听模态框的激活状态,给<body>添加overflow: hidden禁止滚动,同时用calc(100vw - 100%)补偿滚动条宽度避免内容偏移;同时确保模态框自身可滚动(若内容超出视口)。
实现步骤
- 全局CSS样式
/* 当模态框激活时,锁定body滚动并补偿滚动条宽度 */ body:has(.app-modal.is-active) { overflow: hidden; /* 计算滚动条宽度并作为padding-right,防止内容右移 */ padding-right: calc(100vw - 100%); } /* 激活的模态框设置全屏定位,自身内容可滚动 */ .app-modal.is-active { position: fixed; inset: 0; /* 等价于top:0; right:0; bottom:0; left:0 */ overflow-y: auto; background-color: rgba(0,0,0,0.5); /* 可选:添加遮罩背景 */ display: flex; /* 可选:用于居中模态框内容 */ align-items: center; justify-content: center; } /* 模态框内容容器(可选,根据你的组件结构调整) */ .app-modal__content { background-color: #fff; padding: 2rem; max-width: 90%; max-height: 90vh; overflow-y: auto; /* 若内容过多,内部也可滚动 */ }
- Vue组件调整
在AppModal组件中,通过绑定类名控制激活状态:
<template> <div class="app-modal" :class="{ 'is-active': isVisible }"> <div class="app-modal__content"> <!-- 模态框内容 --> <button @click="isVisible = false">关闭</button> </div> </div> </template> <script setup> import { ref } from 'vue' const isVisible = ref(false) // 对外暴露打开/关闭方法,比如defineExpose({ open: () => isVisible.value = true }) </script>
方案优势
- 无JS介入:仅通过CSS
:has()选择器实现状态监听,无需额外JS处理滚动位置或样式补偿 - 保留滚动条占位:用
calc(100vw - 100%)精准补偿滚动条宽度,内容不会因滚动条消失而右移 - 固定滚动位置:未使用
position: fixed,页面会保持当前滚动位置,不会跳转至顶部 - 不干扰其他功能:仅锁定
<body>滚动,不会触发AppHeader的滚动隐藏等逻辑
注意::has()选择器支持Chrome 105+、Firefox 121+、Safari 15.4+,若需兼容旧浏览器,可通过Vue的watch监听模态框状态,动态给<body>添加/移除类(这需要少量JS,但仍是简洁方案)。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

