如何用React结合Vanilla CSS实现类似Google的网页选项菜单?
React实现Google风格选项菜单(Vanilla CSS版)
核心实现思路
通过React状态控制菜单的显示/隐藏,结合原生CSS实现网格布局、定位和交互效果,同时处理点击外部关闭菜单的逻辑。
完整代码实现
1. React组件代码
import { useState, useEffect, useRef } from 'react'; import './GoogleMenu.css'; const GoogleMenu = () => { const [isMenuOpen, setIsMenuOpen] = useState(false); const menuRef = useRef(null); const triggerRef = useRef(null); // 点击外部关闭菜单 useEffect(() => { const handleClickOutside = (e) => { if (menuRef.current && !menuRef.current.contains(e.target) && !triggerRef.current.contains(e.target)) { setIsMenuOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); // 模拟菜单项数据 const menuItems = [ { id: 1, icon: '📧', label: '邮箱' }, { id: 2, icon: '📸', label: '照片' }, { id: 3, icon: '📄', label: '文档' }, { id: 4, icon: '📊', label: '表格' }, { id: 5, icon: '📈', label: '幻灯片' }, { id: 6, icon: '🗺️', label: '地图' }, { id: 7, icon: '🎥', label: 'YouTube' }, { id: 8, icon: '📝', label: '笔记' }, { id: 9, icon: '📅', label: '日历' }, ]; return ( <div className="google-menu-container"> {/* 触发按钮(九宫格图标) */} <button ref={triggerRef} className="menu-trigger" onClick={() => setIsMenuOpen(!isMenuOpen)} aria-label="打开选项菜单" > <svg viewBox="0 0 24 24" width="24" height="24" fill="#5f6368"> <path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/> </svg> </button> {/* 下拉菜单 */} {isMenuOpen && ( <div ref={menuRef} className="menu-dropdown"> <div className="menu-items"> {menuItems.map(item => ( <div key={item.id} className="menu-item"> <span className="item-icon">{item.icon}</span> <span className="item-label">{item.label}</span> </div> ))} </div> </div> )} </div> ); }; export default GoogleMenu;
2. Vanilla CSS样式(GoogleMenu.css)
.google-menu-container { position: relative; display: inline-block; } /* 触发按钮样式 */ .menu-trigger { background: transparent; border: none; cursor: pointer; padding: 8px; border-radius: 50%; transition: background-color 0.2s ease; } .menu-trigger:hover { background-color: rgba(0, 0, 0, 0.05); } .menu-trigger svg { display: block; } /* 下拉菜单容器 */ .menu-dropdown { position: absolute; top: 48px; right: 0; background: white; border-radius: 8px; box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.3), 0 2px 6px 2px rgba(60, 64, 67, 0.15); padding: 16px; width: 320px; max-height: 480px; overflow-y: auto; z-index: 1000; } /* 菜单项网格布局 */ .menu-items { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; } /* 单个菜单项样式 */ .menu-item { display: flex; flex-direction: column; align-items: center; padding: 12px; border-radius: 8px; cursor: pointer; transition: background-color 0.2s ease; color: #3c4043; } .menu-item:hover { background-color: rgba(0, 0, 0, 0.05); } .item-icon { font-size: 32px; margin-bottom: 8px; } .item-label { font-size: 14px; text-align: center; } /* 滚动条样式优化(可选,贴近Google风格) */ .menu-dropdown::-webkit-scrollbar { width: 8px; } .menu-dropdown::-webkit-scrollbar-track { background: transparent; } .menu-dropdown::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.2); border-radius: 4px; } .menu-dropdown::-webkit-scrollbar-thumb:hover { background-color: rgba(0, 0, 0, 0.3); }
关键细节说明
- 外部点击关闭:通过
useRef获取菜单和触发按钮的DOM元素,监听document的点击事件,判断点击位置是否在菜单或按钮外,从而关闭菜单。 - 网格布局:用CSS Grid实现3列网格,自动适配菜单项数量,超出高度时显示滚动条。
- 交互细节:按钮和菜单项的hover效果、阴影样式都贴近Google的设计语言,保证视觉一致性。
- 可扩展性:菜单项通过数组映射生成,可轻松添加/修改条目,图标可替换为SVG或图标库组件。
内容的提问来源于stack exchange,提问作者Miguel Genaro
相关产品推荐
相关产品推荐

