Kalaschnik
Kalaschnik

Reputation: 908

Hover on headlessui Menu element to open instead of click

I am trying to create a Navbar using headlessui’s Menu component. Since hovering is not supported by the component itself, I went through the GitHub issue thread and tried some solutions there. Yet, none of them seems to work without glitching: https://github.com/tailwindlabs/headlessui/issues/239

I build a minimal demo of my navbar (containing nested and untested (direct) links): https://codesandbox.io/s/headlessui-menu-forked-y5q9hm?file=/src/App.js

In here I added onMouseEnter suggested from this SO: Can't make the headlessui dropdown to open on mouse hover and not on click

Yet, it fails to close the menus...

Is there a solution out there?

UPDATE: Naivly, I added an onMouseLeave block and swapped the ternary condition. This seem to work in the first place, yet, the page crashes when switching between two menu items being nested: https://codesandbox.io/s/headlessui-menu-forked-ctfvjm?file=/src/App.js

Upvotes: 0

Views: 1936

Answers (3)

Sahil bakoru
Sahil bakoru

Reputation: 672

In your code you're handling the onMouseEnter and onMouseLeave events to control the menu opening and closing. However, the issue arises when rapidly moving between nested items.

One potential approach to resolve this issue is by introducing a delay in closing the menu on mouse leave. This could prevent accidental closure when moving between menu items.

something like:

  const openMenu = () => {
    setIsOpen(true);
    if (timer) clearTimeout(timer);
  };

  const closeMenu = () => {
    setTimer(setTimeout(() => setIsOpen(false), 200));
  };

 return (


        <div
          onMouseEnter={openMenu}
          onMouseLeave={closeMenu}
          className={`${
            active ? 'bg-blue-500 text-white' : 'bg-white text-black'
          } cursor-pointer p-2`}
        >

Upvotes: 1

B B
B B

Reputation: 81

You will need to move the onMouseLeave event to the parent element if you want users to be able to interact with the submenu, otherwise it will close when moving out of the button to click on a link.

I haven't tested this but it may also help to address the issue of the event failing to close the submenu.

However in practice I tent to avoid using mouse events for hover interactions where possible.

You could solve this with css:

First you would need to remove the condition for rendering the menu items.

Then hide them by default and show them when the button or submenu itself are hovered over.

li > ul[id*=ui-menu-items] {
    display: none;
}

li > button:hover + ul[id*=ui-menu-items],
ul[id*=ui-menu-items]:hover {
    display: block;
}

Upvotes: 0

Steffen Frank
Steffen Frank

Reputation: 1797

It looks like you are nearly there - the only problem remaining in the updated codesandbox seems to be an exception that is raised when onMouseLeave is triggered on the SVG graphics element inside the menu item, which can not react to click events.

The easiest way to fix it is to make the click call null-safe by using use the optional chaining operator:

onMouseEnter={({ target }) => open ? "" : target.click?.()}
onMouseLeave={({ target }) => open ? target.click?.() : ""}

Upvotes: 1

Related Questions