Skip to content

Tooltip flickering in top-left corner of the page when updating options #28

Description

@scriptin

Hello!

I have an issue with dynamic content of my tooltips.

My tooltips can change dynamically based on a condition. The condition changes while the cursor is on the element with the tooltip. For example, this condition changes when I press the button, and my tooltip is on the same button, like this:

<button 
  use:tooltip={cond ? tooltipOptionsA : tooltipOptionsB}
  onclick={() => (cond = !cond)}
>
  My Button
</button>

What I see is that when the condition changes, for an instant my tooltip appears in the top-left corner of a page. This happens because the tooltip element is created (marked with comment "(1)" below) and appended (2) to the DOM (body in my case) in the same function, and only later its position is adjusted (3) in another function (mountTooltip).

From src/lib/tooltip.ts:

	const createTooltip = () => {
		if (Tooltip || visible) return;

		Tooltip = document.createElement('div'); // (1) Creating

		// ... <omit>

		if (!targetElement) {
			document.body.append(Tooltip); // (2) Appending
		} else {
			targetElement.append(Tooltip);
		}
	};
	const mountTooltip = async () => {
		if (!Tooltip && Config.visibility) {

			// ... <omit>

			createTooltip(); // here we initiate the creation of tooltip element (1) and attaching it to the DOM (2)

			// ... <omit>

			cleanUpPosition = autoUpdate(node, Tooltip!, () => {
				if (!Tooltip || !TooltipArrow) return;

				computePosition(node, Tooltip!, {
					// ... <omit>
				}).then(({ x, y, placement, middlewareData }) => {
					Tooltip!.style.left = `${x}px`; // (3) update tooltip container position
					Tooltip!.style.top = `${y}px`;

					// ... <omit>
				});
			});

			// ... <omit>

			Config.onMount?.(); // (4) onMount callback is called
		}
	};

I thought about setting a visibility: hidden class on the container initially and then removing it inside onMount callback which is called inside mountTooltip (4), but I don't have access to the element because it's not passed as an argument. In fact, from onMount I only have access to the configuration and nothing else.

The other option I've explored is using Floating UI middleware to remove the visibility: hidden class, but it doesn't work either because this middleware is called at the wrong moment. I'm not sure if this could possibly work.

Potential solutions:

  1. If @floating-ui/dom allows this, append the tooltip after positioning it, so basically switching the order from 1, 2, 3 to 1, 3, 2. This may not be possible if @floating-ui/dom can properly calculate positions only on elements attached to the DOM, but it would be an ideal solution.
  2. Pass a reference of the tooltip container as an argument into onMount. This way it will be possible to update classes after the tooltip is mounted, and remove the initial visibility: hidden class. Maybe it would be even better to have a separate callback like onPositionSet instead of combining this with the current onMount callback and breaking existing behavior.
  3. If the library itself could add visibility: hidden on a tooltip, and remove it after updating the position, that would also work.

EDIT: After a bit of additional research, I think that option 1 is not really possible, but I added option 3 as a low-effort solution on the library side. (It doesn't exclude option 2 which proposes a change in the API.)

Let me know if you need any additional info.

Thank you!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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