使用Headless UI Disclosure打开侧边菜单时,如何禁用Body滚动以避免双Y轴滚动条
解决Headless UI Disclosure菜单打开时禁用Body滚动的方案
这个问题太常见了,双滚动条确实拉低用户体验,我给你分享几个实际项目中用过的靠谱方案:
方案一:利用Disclosure状态+useEffect精准控制Body样式
核心思路是监听Disclosure的open状态,动态修改Body的CSS属性,同时处理滚动位置防止页面跳转:
import { Disclosure } from '@headlessui/react'; import { useEffect, useState } from 'react'; function SideMenu() { const [scrollPosition, setScrollPosition] = useState(0); return ( <Disclosure> {({ open }) => { useEffect(() => { if (open) { // 记录当前滚动位置,避免关闭菜单后页面跳顶 setScrollPosition(window.scrollY); // 禁用Body滚动,同时固定位置防止内容偏移 document.body.style.overflow = 'hidden'; document.body.style.position = 'fixed'; document.body.style.top = `-${scrollPosition}px`; document.body.style.width = '100%'; // 可选:弥补滚动条消失导致的页面宽度变化 const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth; document.body.style.paddingRight = `${scrollBarWidth}px`; } else { // 恢复Body默认样式 document.body.style.overflow = ''; document.body.style.position = ''; document.body.style.top = ''; document.body.style.width = ''; document.body.style.paddingRight = ''; // 回到之前的滚动位置 window.scrollTo(0, scrollPosition); } }, [open]); return ( <> <Disclosure.Button className="p-2">打开侧边菜单</Disclosure.Button> <Disclosure.Panel className="overflow-auto w-64 h-screen bg-white"> {/* 这里放菜单内容,比如导航项 */} <div className="p-4">菜单选项1</div> <div className="p-4">菜单选项2</div> {/* 更多内容... */} </Disclosure.Panel> </> ); }} </Disclosure> ); }
方案二:结合Tailwind CSS条件类简化操作
如果你用Tailwind,可以把状态提升到父组件,直接给Body或根容器加条件类:
// 根组件(比如App.js) import { useState } from 'react'; import SideMenu from './SideMenu'; function App() { const [isMenuOpen, setIsMenuOpen] = useState(false); return ( <div className={isMenuOpen ? 'overflow-hidden' : ''}> <SideMenu onOpenChange={setIsMenuOpen} /> {/* 页面其他内容 */} </div> ); } // SideMenu组件 import { Disclosure } from '@headlessui/react'; function SideMenu({ onOpenChange }) { return ( <Disclosure onOpenChange={onOpenChange}> {({ open }) => ( <> <Disclosure.Button>打开菜单</Disclosure.Button> <Disclosure.Panel className="overflow-auto w-64 h-screen"> {/* 菜单内容 */} </Disclosure.Panel> </> )} </Disclosure> ); }
优化细节:避免样式闪烁
如果用useEffect偶尔出现短暂的双滚动条闪烁,可以换成useLayoutEffect,它会在DOM渲染完成前执行,样式同步更及时:
import { useLayoutEffect } from 'react'; // 把之前的useEffect替换成useLayoutEffect useLayoutEffect(() => { // 同样的样式控制逻辑 }, [open]);
内容的提问来源于stack exchange,提问作者Tihi
相关产品推荐
相关产品推荐

