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

React NextJS中Tailwind下拉菜单无法从Card组件弹出的问题

问题解决:下拉菜单被Card容器裁剪,无法正常弹出

问题描述

我有一个名为DropdownWithSearch的下拉菜单组件,点击时菜单不会向外弹出,而是被所在的UserAssignForm组件的Card容器包裹,在内部展开。

组件代码

import { Menu, Transition } from '@headlessui/react';
import { Fragment, useState } from 'react';

type DropdownWithSearchProps = {
  users: string[];
};

const DropdownWithSearch = ({ users }: DropdownWithSearchProps) => {
  const [searchTerm, setSearchTerm] = useState('');
  const [isOpen, setIsOpen] = useState(false);

  const toggleDropdown = () => {
    setIsOpen(!isOpen);
  };

  const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setSearchTerm(event.target.value.toLowerCase());
  };

  const filteredUsers = users.filter((user) =>
    user.toLowerCase().includes(searchTerm)
  );

  return (
    <Menu as="div" className="relative">
      <Menu.Button
        className="focus:outline-none inline-flex items-center gap-x-1 text-sm font-semibold leading-6 text-gray-900"
      >
        <button
          onClick={toggleDropdown}
          className="focus:outline-none linear px-10 rounded-xl bg-brand-500 py-3 text-base font-medium text-white transition duration-200 hover:bg-brand-600 active:bg-brand-700 dark:bg-brand-400 dark:text-white dark:hover:bg-brand-300 dark:active:bg-brand-200"
        >
          Select Assignee
        </button>
      </Menu.Button>
      <Transition
        show={isOpen}
        as={Fragment}
        enter="transition ease-out duration-200"
        enterFrom="opacity-0 translate-y-1"
        enterTo="opacity-100 translate-y-0"
        leave="transition ease-in duration-150"
        leaveFrom="opacity-100 translate-y-0"
        leaveTo="opacity-0 translate-y-1"
      >
        <Menu.Items
          static
          className="origin-top-left absolute left-0 mt-2 w-full max-w-md rounded-3xl shadow-lg bg-white ring-1 ring-gray-900/5 focus:outline-none z-50"
        >
          <div className="p-4">
            <input
              onChange={handleSearchChange}
              className="block w-full px-4 py-2 mb-4 text-gray-800 border rounded-md border-gray-300 focus:outline-none"
              type="text"
              placeholder="Search assignees"
              autoComplete="off"
            />
            {filteredUsers.map((user, index) => (
              <Menu.Item key={index}>
                {({ active }) => (
                  <a
                    href="#"
                    className="group relative flex gap-x-6 rounded-lg p-4 hover:bg-gray-50 cursor-pointer focus:outline-none"
                    onClick={(e) => {
                      // e.preventDefault();
                      // handleAssigneeSelect(assignee);
                    }}
                  >
                    <span className="font-semibold text-gray-900">{user}</span>
                  </a>
                )}
              </Menu.Item>
            ))}
          </div>
        </Menu.Items>
      </Transition>
    </Menu>
  );
};

export default DropdownWithSearch;

UserAssignForm组件

import Card from "components/card";
import { useState } from 'react';
import DropdownWithSearch from './DropdownWithSearch';

export default function UserAssignForm() {
    const [selectedAssignee, setSelectedAssignee] = useState('');
    const userList = ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank', 'Grace', 'Hank', 'Ivy', 'Jack'];

    return (
        <Card extra="mb-3 w-full h-full px-6 pb-6 sm:overflow-x-auto relative">
            <div className="relative flex items-center justify-between pt-4">
                <div className="text-xl font-bold text-navy-700 dark:text-white">
                    Assign Users
                </div>
            </div>
            <div className="mt-8 overflow-x-scroll xl:overflow-x-hidden">
                <div className="mt-5 mb-5 grid h-full grid-cols-1 gap-5 md:grid-cols-2">
                    <div className="flex justify-center flex-row pl-3 pt-1">
                        <DropdownWithSearch
                        users={userList}/>
                        <input
                            disabled={true}
                            type="text"
                            id="assigneeInput"
                            placeholder="Selected Assignee"
                            value={selectedAssignee}
                            onChange={(e) => setSelectedAssignee(e.target.value)}
                            className={`cursor-not-allowed ml-4 w-100 flex h-12 items-center justify-center rounded-xl border bg-white/0 p-3 text-sm outline-none `}
                        />
                    </div>
                    {/*...*/}
                </div>
                <div className="flex justify-center">
                    <button
                        className="linear px-10 rounded-xl bg-brand-500 py-3 text-base font-medium text-white transition duration-200 hover:bg-brand-600 active:bg-brand-700 dark:bg-brand-400 dark:text-white dark:hover:bg-brand-300 dark:active:bg-brand-200"
                    // onClick={handleSubmit}
                    >
                        Assign
                    </button>
                </div>
            </div>
        </Card>
    );
}

问题原因及解决办法

核心问题1:Menu.Items的static属性

Headless UI的Menu.Items默认是absolute定位,但你给它加了static属性,这会强制它变成静态定位(正常文档流元素),导致下拉菜单被Card容器的overflow-x-scroll和h-full样式裁剪,无法跳出容器。

解决:移除Menu.Items上的static属性
修改DropdownWithSearch中的Menu.Items部分:

<Menu.Items
  className="origin-top-left absolute left-0 mt-2 w-full max-w-md rounded-3xl shadow-lg bg-white ring-1 ring-gray-900/5 focus:outline-none z-50"
>

核心问题2:Card容器的overflow样式

UserAssignForm中的Card内部有overflow-x-scroll和h-full,这会让容器内部超出部分被裁剪。如果必须保留这些样式,需要让下拉菜单相对于Card外部的元素定位:

可选优化:调整Dropdown的定位上下文
如果Card的overflow无法移除,可以给DropdownWithSearch的外层容器(或者UserAssignForm的某个上层元素)添加relative,同时确保Dropdown的z-index足够高(已经设置了z-50,一般足够)。

次要问题:拼写错误

Card组件的extra属性里reletive是拼写错误,改为relative,确保定位上下文正确。

最终效果

修改后,下拉菜单会以DropdownWithSearch的外层relative容器为基准,absolute定位跳出Card容器,正常弹出显示。

内容的提问来源于stack exchange,提问作者Damika

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 11:22:09