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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 11:05:22