Skip to content

Bug: Tooltip gets stuck in open state due to early return when popperTriggers includes 'hover' #1081

@Jioho

Description

@Jioho

While using floating-vue, I encountered an issue where tooltips can get stuck in an open state under certain edge cases. This behavior is similar to what was discussed in #860.

After investigation, I traced the issue to the following code:

main: https://github.com/Akryum/floating-vue/blob/main/packages/floating-vue/src/components/Popper.ts#L477
vue2: https://github.com/Akryum/floating-vue/blob/vue2/packages/floating-vue/src/components/Popper.ts#L451

// Abort if aiming for the popper
if (!skipAiming && this.hasPopperShowTriggerHover && this.$_isAimingPopper()) {
  return
}

This early return prevents the execution of any logic below — including both the tooltip close logic and any parent popper state updates. As a result, when $_isAimingPopper() returns true, the tooltip may remain open indefinitely, since no further conditions are evaluated and no close action is triggered.

Analysis

The original intention of checking $_isAimingPopper() is understandable — to avoid hiding the tooltip when the user is moving the cursor toward the popper. However, by returning early here, it skips necessary logic that ensures the tooltip can recover or close later.

Would it be more appropriate to restructure this logic like this?

if (this.parentPopper) {
  if (this.hasPopperShowTriggerHover && this.$_isAimingPopper()) {
    clearTimeout(this.parentPopper.lockedChildTimer)
    this.parentPopper.lockedChildTimer = setTimeout(() => {
      if (this.parentPopper.lockedChild === this) {
        this.parentPopper.lockedChild.hide({ skipDelay })
        this.parentPopper.lockedChild = null
      }
    }, 1000)
    return
  }
}

This ensures that even if $_isAimingPopper() returns true, the locking behavior is correctly handled.

Suggestion

Consider moving the $_isAimingPopper() check inside the this.parentPopper condition;

Or refactor the logic to make sure the locking behavior is not bypassed due to early return;

If this behavior is intentional, could you clarify the design purpose and whether a fallback mechanism can be added to recover the tooltip state?

Reproduction

You can refer to the demo linked in the existing issue: #860

Thanks a lot for maintaining this great library 🙌

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions