FezVrasta/popper.js

View on GitHub
website/pages/docs/useHover.mdx

Summary

Maintainability
Test Coverage
<PageCard>

# useHover

Opens the floating element while hovering over the reference
element, like CSS `:hover`.

```js
import {useHover} from '@floating-ui/react';
```

Includes the ability to enter the floating element
[without it closing](/docs/useHover#safepolygon).

<ShowFor packages={['react-dom']}>

<PackageLimited>@floating-ui/react only</PackageLimited>

</ShowFor>

</PageCard>

## Usage

This Hook returns event handler props.

To use it, pass it the `context{:.const}` object returned from
`useFloating(){:js}`, and then feed its result into the
`useInteractions(){:js}` array. The returned prop getters are
then spread onto the elements for rendering.

```js {9-13} /context/
function App() {
  const [isOpen, setIsOpen] = useState(false);

  const {refs, floatingStyles, context} = useFloating({
    open: isOpen,
    onOpenChange: setIsOpen,
  });

  const hover = useHover(context);

  const {getReferenceProps, getFloatingProps} = useInteractions([
    hover,
  ]);

  return (
    <>
      <div ref={refs.setReference} {...getReferenceProps()}>
        Reference element
      </div>
      {isOpen && (
        <div
          ref={refs.setFloating}
          style={floatingStyles}
          {...getFloatingProps()}
        >
          Floating element
        </div>
      )}
    </>
  );
}
```

<Notice>

Due to a bug with React's event delegation system, some event
handlers are assigned directly on the reference element ref. This
means `useHover(){:js}` will appear to work without spreading any
props onto the reference element (or passing it into the
interactions array).

However, not all features will work, and bugs can occur, so
ensure you always spread the props onto the reference element.

</Notice>

## Examples

- [Default hover](https://codesandbox.io/s/late-https-lu3833?file=/src/App.tsx)

## Props

```ts
interface UseHoverProps {
  enabled?: boolean;
  mouseOnly?: boolean;
  delay?: number | Partial<{open: number; close: number}>;
  restMs?: number;
  move?: boolean;
  handleClose?: null | HandleCloseFn;
}
```

### `enabled{:.key}`

default: `true{:js}`

Conditionally enable/disable the Hook.

```js
useHover(context, {
  enabled: false,
});
```

This is also useful when you want to disable further events from
firing based on some condition. For example, you may disable the
hook after hovering over the floating element to then prevent it
from closing.

### `mouseOnly{:.key}`

default: `false{:js}`

Whether the logic only runs for mouse input, ignoring both touch
and pen pointer inputs.

```js
useHover(context, {
  mouseOnly: true,
});
```

### `delay{:.key}`

default: `0{:js}`

Waits for the specified time when the event listener runs before
changing the `open{:.const}` state.

```js
useHover(context, {
  // Delay opening or closing the floating element by 500ms.
  delay: 500,

  // Configure the delay for opening and closing separately.
  delay: {
    open: 500,
    close: 0,
  },
});
```

### `restMs{:.key}`

default: `0{:js}` (off)

Waits until the user's cursor is at "rest" over the reference
element before changing the open state.

```js
useHover(context, {
  // The user's cursor must be at rest for 150ms before opening.
  restMs: 150,
});
```

You can also use a fallback delay if the user's cursor never
rests, to ensure the floating element will eventually open:

```js
useHover(context, {
  restMs: 150,
  // If their cursor never rests, open it after 1000ms as a
  // fallback.
  delay: {open: 1000},
});
```

### `move{:.key}`

default: `true{:js}`

Whether moving the cursor over the floating element will open it,
without a regular hover event required.

For example, if it was resting over the reference element when it
closed. Uses the `'mousemove'{:js}` event.

```js
useHover(context, {
  move: false,
});
```

### `handleClose{:.key}`

default: `null{:js}`

Instead of closing the floating element when the cursor leaves
its reference, we can leave it open until a certain condition is
satisfied.

The package exports a `safePolygon(){:js}` handler which will
only close the floating element if the pointer is outside a
dynamically computed polygon area.

This allows the user to move the cursor off the reference element
and towards the floating element without it closing (e.g. it has
interactive content inside).

```js
import {useHover, safePolygon} from '@floating-ui/react';

useHover(context, {
  handleClose: safePolygon(),
});
```

This handler runs on `mousemove{:.string}`.

For a simpler alternative, depending on the type of floating
element, you can use a short close delay instead.

## safePolygon

A "safe" polygon is one that a pointer is safe to traverse as it
moves off the reference element and toward the floating element
after hovering it. If the pointer moves outside of this safe
area, the floating element closes.

It is a dynamic polygon (either a rect or a triangle) originating
from the cursor once it leaves a reference element. The triangle
looks like this:

<video
  className="rounded-lg shadow"
  src="/safe-polygon.mp4"
  autoPlay
  loop
  muted
/>

This function takes options.

### `requireIntent{:.key}`

default: `true{:js}`

Determines whether intent is required for the triangle polygon to
be generated (that is, the cursor is moving quickly enough toward
the floating element). `false{:js}` will keep the triangle active
no matter the intent.

```js
useHover(context, {
  handleClose: safePolygon({
    requireIntent: false,
  }),
});
```

When reference elements are placed near each other and they each
have a hoverable floating element attached, `true{:js}` ensures
that hover events for the other nearby references aren't too
aggressively blocked.

### `buffer{:.key}`

default: `0.5{:js}`

Determines the amount of buffer (in pixels) there is around the
polygon.

While the default value should handle the vast majority of cases
correctly, if you find your floating element is closing
unexpectedly as the pointer tries to move toward the floating
element, try increasing this value.

```js
useHover(context, {
  handleClose: safePolygon({
    buffer: 1,
  }),
});
```

#### Ignoring the triangle

If you only want the offset portion (rectangle bridge) between
the reference and floating elements to be considered, you can set
the value to `-Infinity{:js}`.

```js
useHover(context, {
  handleClose: safePolygon({
    // Don't generate a triangle polygon, only consider the
    // rectangular bridge between the elements.
    buffer: -Infinity,
  }),
});
```

### `blockPointerEvents{:.key}`

default: `false{:js}`

Whether CSS `pointer-events` behind the polygon, reference, and
floating elements are blocked. This ensures the user does not
fire hover events over other elements unintentionally while they
traverse the polygon.

```js
useHover(context, {
  handleClose: safePolygon({
    blockPointerEvents: true,
  }),
});
```

This can cause container elements that listen for `mouseleave`
events to fire. In older versions of Chrome (&lt;114), scrolling
containers can't be scrolled while the pointer is over the
floating element (the main window remains unaffected).

A `[data-floating-ui-safe-polygon]{:.keyword}` selector is
available as a parent, so scrolling containers can negate the
`pointer-events{:.function}` style:

```css
[data-floating-ui-safe-polygon] .scroll {
  pointer-events: auto;
}

[data-floating-ui-safe-polygon] .scroll > div {
  pointer-events: none;
}
```

```js
<div className="scroll">
  <div>
    Content inside here will remain blocked without affecting the
    scrolling parent.
  </div>
</div>
```