Vize

element-hover

Track whether the pointer hovers an element.

Package @vizejs/composable/element-hover
Own the source vize lib pull composable:element-hover
Runtime exports useElementHover
Gzip budget 2048 B

Minimal setup

Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).

vp install @vizejs/composable

Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.

import { useElementHover } from "@vizejs/composable/element-hover";

Try it

Show delayed guidance for a selected card

Delay pointer guidance for a real reactive element while keeping the advice independently available to keyboard and tap users.

Open the interactive example

What to observe: Move over the selected guide card for 300 ms to reveal the advice. Departure retains it for 200 ms; returning cancels the pending departure. Choose the other card to remove the previous target's listeners and reset the delayed flag. Show persistent guidance keeps the same advice visible without hovering.

useElementHover receives a computed native section target, with explicit delayEnter and delayLeave. pointerenter/pointerleave ignore movement among descendants. Opposite events cancel pending delay, and reactive target replacement removes old listeners, cancels its timer, and resets the hover flag. The output deliberately says Delayed pointer hover rather than instantaneous pointer position. The persistent guidance button owns separate state and works with Enter, Space, or ordinary tapping. SSR starts false and no target exists; the template refs resolve on the client. Scope disposal removes listeners and pending timers. No advice depends exclusively on hover.

Compare the initial and interacted states
Show delayed guidance for a selected card: initial state
Initial state
Show delayed guidance for a selected card: state after the verified interactions
After the verified interactions

Copy the example

Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.

<script setup lang="ts">
import { computed, ref, useId, useTemplateRef } from "vue";
import { useElementHover } from "@vizejs/composable/element-hover";

const cardsId = useId();
const first = useTemplateRef<HTMLElement>("first");
const second = useTemplateRef<HTMLElement>("second");
const selected = ref("first");
const target = computed(() => (selected.value === "first" ? first.value : second.value));
const hovered = useElementHover(target, { delayEnter: 300, delayLeave: 200 });
const pinned = ref(false);
const guidanceVisible = computed(() => hovered.value || pinned.value);
</script>

<template>
  <div class="composable-example">
    <label :for="cardsId">Guidance target</label>
    <select :id="cardsId" v-model="selected">
      <option value="first">Compiler guide card</option>
      <option value="second">Keyboard guide card</option>
    </select>
    <div class="guide-cards">
      <section ref="first" class="guide-card" aria-label="Compiler guide card">
        <strong>Compiler guide</strong>
        <p>Compile one representative component.</p>
      </section>
      <section ref="second" class="guide-card" aria-label="Keyboard guide card">
        <strong>Keyboard guide</strong>
        <p>Review native input and focus.</p>
      </section>
    </div>
    <button type="button" :aria-pressed="pinned" @click="() => (pinned = !pinned)">
      {{ pinned ? "Hide persistent guidance" : "Show persistent guidance" }}
    </button>
    <output aria-live="polite">
      Delayed pointer hover: {{ hovered ? "yes" : "no" }} · Persistent guidance:
      {{ pinned ? "yes" : "no" }}
    </output>
    <p v-if="guidanceVisible">
      Review the selected card's behavior, diagnostics, and native controls.
    </p>
    <p>
      Pointer guidance waits 300 ms on entry and 200 ms on departure. Switching the selected card
      removes the previous listeners and pending delay. The native guidance button independently
      keeps the same advice available to keyboard and tap users.
    </p>
  </div>
</template>

<style scoped>
.guide-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
  gap: 12px;
}
.guide-card {
  padding: 12px;
  border: 1px solid currentColor;
  border-radius: 8px;
}
</style>

Runtime contract

Utility Category Stability SSR Hydration Cleanup Targets Host globals Uses
useElementHover input experimental deterministic-fallback stable reactive-scope web, desktop globalThis resolveElement, tryOnScopeDispose

API

useElementHover

Track whether the pointer hovers an element. Uses pointerenter/pointerleave (which ignore descendants) with optional enter/leave delays; a pending delay is cancelled by the opposite event. Server renders report false; listeners and timers are released with the owning reactive scope.

function useElementHover( target: MaybeElementTarget, options: UseElementHoverOptions = {}, ): Readonly<Ref<boolean>>

Types

UseElementHoverOptions

Options for useElementHover.

Member Type Description
delayEnter? number Milliseconds to wait before reporting hover.
delayLeave? number Milliseconds to wait before reporting the end of hover.
scheduler? TimeoutScheduler Owns the delay timer.