how to trigger popover button on "hover" event instead of "onClick" event. #425
Replies: 36 comments 45 replies
-
Seconding this issue. Curious how this is accomplished. |
Beta Was this translation helpful? Give feedback.
-
Also have this issue. Currently it's a blocker to make headless UI ready for production. |
Beta Was this translation helpful? Give feedback.
-
I am having the same issue |
Beta Was this translation helpful? Give feedback.
-
Also and issue here. Would be great to support this. |
Beta Was this translation helpful? Give feedback.
-
Same issues here! i resolved this issue by working on reactjs and making my own dropdown solution |
Beta Was this translation helpful? Give feedback.
-
Hey Guys, The simplest way I found to achieve this is as follows:
Then on the menu item that shows in the navbar I add the following to manage the hover events:
Finally, on the HeadlessUi Transition I add the show property and use the current state to trigger it. Also, you will need to add the hover event triggers to stop the flyout menu disappearing when hovering over it:
Hope this helps. |
Beta Was this translation helpful? Give feedback.
-
Also and issue here. Would be great to support this. |
Beta Was this translation helpful? Give feedback.
-
Adding another vote for supporting this, either natively or by exposing the underlying state more completely (it would be great to be able to directly set the state of the Popover). The preferred solution above appears to have accessibility issues, breaking the component's ability to accurately control We have implemented a slightly hackier workaround that forces a click event to trigger when a user hovers. Here's the function we came up with:
(Note: parts of the above may be overkill depending on what you're trying to do. We have a list where each item can trigger a popover, which itself is positioned slightly away from the containing element, so we're having to account for mouse travel between two elements and tracking the state of which popover is currently open 😅 ) In order to ensure popovers close when a user clicks somewhere else on the screen, we have a second function:
And then the following event handlers on the Popover components themselves:
This setup is a lot more complicated, and it does still have some small issues in terms of accurately tracking state, but it does preserve the accessibility of the components, and provides the same UX as native hover events would. |
Beta Was this translation helpful? Give feedback.
-
Please, I need this |
Beta Was this translation helpful? Give feedback.
-
I found this, which seems to be a pretty good implementation: https://codesandbox.io/s/d8llw?file=/src/App.js |
Beta Was this translation helpful? Give feedback.
-
has anyone solved the issues or any videos out there explain how to do it? |
Beta Was this translation helpful? Give feedback.
-
so what is the best solution for this? anyone? I have hard time understanding the code from https://codesandbox.io/s/d8llw?file=/src/App.js |
Beta Was this translation helpful? Give feedback.
-
A little off-topic, but it's conceptually adjacent to a hover-triggered popover, and Googling for "headlessui tooltip" lead me here as one of the top results, so I'm putting this here in case this helps someone. I was in need of a tooltip component (I'm currently migrating from Chakra to Tailwind + Headless UI), and figured I could just re-purpose the Unfortunately, the lack of ability to externally control the Popover state, in addition to the whole button-click thing with In the end it was easier to just do it with It ain't much, but it does the trick: import type React from "react";
import type * as PopperJS from "@popperjs/core";
import { useCallback, useRef, useState } from "react";
import { usePopper } from "react-popper";
type TooltipProps = {
label: React.ReactElement;
placement?: PopperJS.Placement;
enterDelay?: number;
leaveDelay?: number;
} & React.HTMLAttributes<HTMLDivElement>;
export const Tooltip: React.FC<TooltipProps> = (props) => {
const {
children,
label,
enterDelay = 250,
leaveDelay = 150,
placement = "bottom",
} = props;
const [isOpen, setIsOpen] = useState(false);
let [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(
null
);
let [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
let { styles, attributes } = usePopper(referenceElement, popperElement, {
placement,
modifiers: [{ name: "offset", options: { offset: [0, 4] } }],
});
let enterTimeout = useRef<NodeJS.Timeout>();
let leaveTimeout = useRef<NodeJS.Timeout>();
const handleMouseEnter = useCallback(() => {
leaveTimeout.current && clearTimeout(leaveTimeout.current);
enterTimeout.current = setTimeout(() => setIsOpen(true), enterDelay);
}, [enterDelay]);
const handleMouseLeave = useCallback(() => {
enterTimeout.current && clearTimeout(enterTimeout.current);
leaveTimeout.current = setTimeout(() => setIsOpen(false), leaveDelay);
}, [leaveDelay]);
return (
<div>
<div
ref={setReferenceElement}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
className="relative"
>
{children}
</div>
<div
ref={setPopperElement}
style={styles.popper}
{...attributes.popper}
className={`transition-opacity ${isOpen ? "opacity-100" : "opacity-0"}`}
>
{label}
</div>
</div>
);
}; Example usage could be something like: <Tooltip
label={
<span className="flex items-center bg-slate-700 text-white text-sm font-medium py-1 px-2 rounded-sm shadow-md">
This is some helpful text
</span>
}
>
<span>I have a tooltip!</span>
</Tooltip> Count this as a +1 for the ability to have more fine-grained control over the |
Beta Was this translation helpful? Give feedback.
-
+1 Another vote on supporting this hover behavior natively in headlessUI. This is what I am trying to accomplish using headlessUI and tailwindcss: threads_mega_menu_hover.mov |
Beta Was this translation helpful? Give feedback.
-
Here's my approach to handle hover with a popover button: <Popover className="relative inline-block select-none ml-10 mr-10">
<Popover.Button
onClick={(event) => handlePopoverClick(event)}
onMouseEnter={(ref) => handlePopoverEnter(ref)}
onMouseOut={(ref) => handlePopoverOut(ref)}
dataActive="false"
>
Your Button Content
</Popover.Button>
<Popover.Panel className="absolute z-10 left-1/2 transform -translate-x-1/2 bottom">
<div className="bg-white dark-bg-neutral-900 p-4 border border-neutral-700 rounded-2xl min-w-[300px]">
<div className="text-xs">
<div>
<span>Details: [Your custom details here]</span>
</div>
<div>
<span>Time: [Your custom time here]</span>
</div>
</div>
</div>
</Popover.Panel>
</Popover> export function handlePopoverEnter(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
const buttonRef = event.target as HTMLButtonElement;
if (buttonRef) buttonRef.click();
};
export function handlePopoverOut(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
const buttonRef = event.target as HTMLButtonElement;
if (buttonRef) {
const isActive = buttonRef.getAttribute('data-active') === 'true';
if (isActive) buttonRef.click();
}
}
export function handlePopoverClick(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
const buttonRef = event.currentTarget as HTMLButtonElement;
if (buttonRef) {
const isActive = buttonRef.getAttribute('data-active') === 'true';
buttonRef.setAttribute('data-active', isActive ? 'false' : 'true');
}
} |
Beta Was this translation helpful? Give feedback.
-
I am trying to build a Navbar with the Menu component. It seems all solutions provided here or in the feature request, are not working great when having multiple items. I created a minimal Navbar demo here (with nested and unnested entries): https://codesandbox.io/s/headlessui-menu-forked-y5q9hm?file=/src/App.js As of now, is there any unbloated version out there that works reliably? |
Beta Was this translation helpful? Give feedback.
-
This post on Medium shows how to do this. Toggle NextUI Dropdown on Hover |
Beta Was this translation helpful? Give feedback.
-
Hint create separate component for PopoverButton, i.e. CustomPopoverButton
and use it like this
|
Beta Was this translation helpful? Give feedback.
-
I tried all solution but decided to use this amazing library instead; |
Beta Was this translation helpful? Give feedback.
-
Can't believe such basic, obviously needed reason never implemented in 3 years. |
Beta Was this translation helpful? Give feedback.
-
Anything on this yet? |
Beta Was this translation helpful? Give feedback.
-
I'm a relatively new web developer and I'm about to give up on Next-UI. It seems to be full of little bugs and incomplete features like this. There's got to be something better out there. I cannot image people having to deal with this kind of endless non-sense to do the most basic things. |
Beta Was this translation helpful? Give feedback.
-
Hello, guys i hope You All Are doing Well |
Beta Was this translation helpful? Give feedback.
-
I ran into this problem earlier today, and figured I would share my solution that seems to work fairly well (note, this is using Popover v2.1, so mileage may vary). https://headlessui.com/react/popover#popover-button The way I fixed this is by adding an observer to the popover in question, there are data attributes called I hope this helps!
|
Beta Was this translation helpful? Give feedback.
-
For anyone stumbling upon this, below is a solution after hours of investigating, looking into typings, aria etc. Unfortunately, as of v2.2.0 you're not going to find a way to tap into Not sure if this is a less hacky solution than @nosovj 's This is the approach via a render prop: import { Popover } from '@headlessui/react';
function () {
return (
<Popover>
{({ open, close }) => <PopoverHoverer isOpen={open} close={close} />
</Popover
);
} import { PopoverButton, PopoverPanel, Transition } from '@headlessui/react';
const DEBOUNCE = 100;
function PopoverHoverer({ children, isOpen, close }: PropsWithChildren<{ isOpen: boolean; close: () => void }>) {
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const clear = () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
const handleMouseEnter = () => {
clear();
if (!isOpen) {
buttonRef.current?.click();
}
};
const handleMouseLeave = () => {
clear();
timeoutRef.current = setTimeout(() => {
close();
}, DEBOUNCE);
};
useEffect(() => {
return () => {
clear();
};
}, [close]);
return (
<>
{/* Ensure your custom component sets an `onClick` prop (even if it's a no-op function)
otherwise `event.preventDefault()` will be called and default browser behaviour won't work
@see https://github.com/tailwindlabs/headlessui/issues/3561 */}
<PopoverButton
as={MyLinkComponent}
href="/"
ref={buttonRef}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
My Link
</PopoverButton>
<Transition show={isOpen}>
<PopoverPanel onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
{children}
</PopoverPanel>
</Transition>
</>
);
} Honestly, given all the search results on this issue out there, I think @RobinMalfait & team should reconsider the approach here and expose |
Beta Was this translation helpful? Give feedback.
-
by the way, headless-ui already has as a dependency |
Beta Was this translation helpful? Give feedback.
-
How can this be still a problem. We just want an option onHover true/false. |
Beta Was this translation helpful? Give feedback.
-
Same! Bump guys |
Beta Was this translation helpful? Give feedback.
-
I'm new to tailwind CSS and using headless-ui/react for my Next Js project.
I want to open these navigation menus on hover but I couldn't find any solution please help me out for that.
Beta Was this translation helpful? Give feedback.
All reactions