ReactJS+Tailwind CSS实现点击页面外部关闭Dropdown菜单
实现点击外部区域关闭Dropdown菜单
核心修改要点:
- 移除外层容器的
onClick绑定,避免点击下拉菜单内部误触发关闭 - 使用
useEffect监听全局点击事件,判断点击区域是否在Dropdown相关元素外,实现外部点击关闭 - 给下拉菜单容器添加点击事件阻止冒泡,防止点击菜单内容时触发全局关闭逻辑
修改后的完整代码:
import React, { useState, useEffect, useRef } from "react"; const App = () => { const [showOptions, setShowOptions] = useState(false); const dropdownRef = useRef(null); const buttonRef = useRef(null); const handleClick = () => { setShowOptions(!showOptions); }; useEffect(() => { const handleOutsideClick = (e) => { // 判断点击的元素不在按钮和下拉框内时,关闭菜单 if (dropdownRef.current && !dropdownRef.current.contains(e.target) && !buttonRef.current.contains(e.target)) { setShowOptions(false); } }; // 当菜单展开时绑定全局点击事件 if (showOptions) { document.addEventListener('click', handleOutsideClick); } // 组件卸载或菜单关闭时移除事件监听 return () => { document.removeEventListener('click', handleOutsideClick); }; }, [showOptions]); // 阻止下拉菜单内部点击触发全局关闭 const handleDropdownClick = (e) => { e.stopPropagation(); }; return ( <div className="relative inline-block text-left"> <div> <button ref={buttonRef} onClick={handleClick} type="button" id="menu-button" aria-expanded={showOptions} aria-haspopup="true" > <div> <section className="inline-flex gap-1 items-center text-left cursor-pointer"> <h3 className="text-xs text-[#797979]">Welcome</h3> <svg className="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> <path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd" /> </svg> </section> </div> </button> </div> {showOptions ? ( <div ref={dropdownRef} onClick={handleDropdownClick} className="absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none overflow-y-auto z-[1000]" role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1" > <div className="py-1" role="none"> <div className="items-center"> <div className="text-gray-700 block px-4 py-2 text-sm hover:bg-gray-100"> <a href="">Info 1</a> </div> </div> <div className="items-center"> <div className="text-gray-700 block px-4 py-2 text-sm hover:bg-gray-100"> <a href="">Info 2</a> </div> </div> <div className="items-center"> <div className="text-gray-700 block px-4 py-2 text-sm hover:bg-gray-100"> <a href="">Info 3</a> </div> </div> </div> </div> ) : null} </div> ); }; export default App;
关键修改说明:
- 添加
useRef:分别给按钮和下拉菜单容器创建ref,用来判断点击区域是否属于这两个元素 - 全局点击监听:通过
useEffect在菜单展开时绑定document的点击事件,点击外部时关闭菜单;菜单关闭或组件卸载时移除事件,避免内存泄漏 - 阻止内部冒泡:给下拉菜单添加
onClick事件并调用e.stopPropagation(),防止点击菜单内容时触发全局关闭逻辑 - 修正
aria-expanded属性:将固定值true改为动态的showOptions,符合无障碍规范 - 统一
class为className:React中需用className替代原生HTML的class,原代码中部分混用,已统一修正
内容的提问来源于stack exchange,提问作者Anis Indah
相关产品推荐
相关产品推荐

