react-tooltip icon indicating copy to clipboard operation
react-tooltip copied to clipboard

[BUG] Tooltip position and show status not synchronized.

Open knubie opened this issue 2 years ago • 32 comments

Describe the bug When moving from one anchor to another, sometimes the tooltip's position is updated before its visibility. This results in some glitchy / flickering behavior.

Version of Package v5.10.6

To Reproduce

  1. Set Tooltip delayShow to 300
  2. Set Tooltip delayHide to 0
  3. Move from one anchor to another (with no space between)
  4. Observe that the tooltip first changes position and content, then hides. Then after 300ms shows again.

Expected behavior The tooltip should hide first, then update position and content.

Screenshots https://user-images.githubusercontent.com/431251/230918927-995f1ccf-2449-40d8-8a4b-948c48605393.mp4

Desktop (please complete the following information if possible or delete this section):

  • OS: MacOS
  • Browser firefox
  • Version [e.g. 22]
  • Frameworks create-react-app

Additional context Repro: https://github.com/knubie/react-tooltip-demo Somewhat, but not really related: https://github.com/ReactTooltip/react-tooltip/issues/1008

knubie avatar Apr 10 '23 14:04 knubie

This one will be a lot trickier to solve. The root of this problem is that the tooltip content and the tooltip position are updated independently. Since the position calculation may take more than 1 render cycle to finish, and updating the content will pretty much always take exactly 1 cycle, sometimes you'll see the text updating before the position.

We've tried to fix this by updating the content and the position on the same cycle 100% of the time, but it turned out to be not that easy.

The tooltip closing when you move from one element to another is actually a bug, since the expected behavior when using delayShow is that the tooltip will stay open for as long as you're hovering an anchor element, not close and reopen after the delay.

Here are some alternatives until we can tackle this on (it might take several days until we have the time):

  1. When using delayShow, also use delayHide with a small value (50 should be fine). This will mitigate the flickering, though sometimes you'll still see the text updating before the tooltip has moved.
  2. Use one tooltip for each anchor element. This is really not ideal, and we usually recommend against this, but it could work on your case if you don't have many elements (anything up to 20 tooltips on the page should be fine, maybe even more).

If you have any other ideas, please let us know.

gabrieljablonski avatar Apr 10 '23 14:04 gabrieljablonski

The tooltip closing when you move from one element to another is actually a bug, since the expected behavior when using delayShow is that the tooltip will stay open for as long as you're hovering an anchor element, not close and reopen after the delay.

Ah okay. I was going to ask about this earlier. Solving that bug might be a good next step.

  1. When using delayShow, also use delayHide with a small value (50 should be fine). This will mitigate the flickering, though sometimes you'll still see the text updating before the tooltip has moved.

This worked for me. However I noticed another issue that is kind of the opposite of the previous one: When hovering over an anchor, the tooltip's show state is updated to true first, then a frame or two later the position and content are updated. This has the effect of the tooltip showing up at it's previous position for a frame or two before moving to the correct position. However I was only able to replicate this in my own application and not the create-react-app demo.

To resolve that I simply added the transition CSS property back in so that the tooltip is opacity: 0 for the first frame or two.

Ideally all of the state change (show/position/content) should update synchronously. I don't have any concrete suggestions for you at the moment, though, as I'm not that familiar with the code base.

knubie avatar Apr 10 '23 20:04 knubie

This issue is stale because it has not seen activity in 30 days. Remove the stale label or comment within 14 days, or it will be closed.

github-actions[bot] avatar May 12 '23 12:05 github-actions[bot]

I'm experiencing a similar issue.

https://github.com/ReactTooltip/react-tooltip/assets/25497078/0babc57b-2d85-4ec4-afe1-a971846cf213

Looks like the position is not updated before the tooltip is shown

dbidwell94 avatar Jun 30 '23 18:06 dbidwell94

Also experiencing this on the latest version, even with the transition hack mentioned above. Haven't had time to dig into how or why it's happening, though.

knubie avatar Jul 01 '23 02:07 knubie

I've been digging, but I can't seem to find the problem yet.

dbidwell94 avatar Jul 01 '23 18:07 dbidwell94

@dbidwell94 the original issue has to do with transitioning between two adjancent anchor elements, yours seems to be a little different. I am unable to reproduce it. If you could provide a sample repo it would help a lot.

@knubie are you experiencing the same thing? as in:

  • you hover on an element to show the tooltip
  • you hover out and the tooltip is hidden
  • you hover on another element and the tooltip is misplaced for a few frames

Again, I haven't been able to reproduce this.

gabrieljablonski avatar Jul 01 '23 19:07 gabrieljablonski

Just recorded a demo video from my production app. I noticed the following things:

  • On mouseout the tooltip is misplaced for a few frames.
  • On mouseenter the tooltip is misplaced for ~1 frame sometimes. This only happens once or twice in the video.

Here are the settings for my tooltip:

delay-show = 500
delay-hide = 50
transition: opacity 0.1s ease-out

https://github.com/ReactTooltip/react-tooltip/assets/431251/42e2b4f8-0e52-4a5a-98a4-2d2244e37d3d

knubie avatar Jul 01 '23 23:07 knubie

@gabrieljablonski Here are the items in question:

React tooltip instance => https://github.com/Tekxchange/web_ui/blob/master/src/App.tsx#L26

Go to current location data-id => https://github.com/Tekxchange/web_ui/blob/master/src/pages/app/map/index.tsx#L86 Results bar id => https://github.com/Tekxchange/web_ui/blob/master/src/pages/app/resultsBar/index.tsx#L41

I'm not sure it's too big of a deal. While testing I have noticed it:

A) only seems to happen on Opera (might happen on Safari as well, I'm not sure) B) only happens on that browser if you are logged in C) Chrome and Firefox seem to be working as expected.

dbidwell94 avatar Jul 02 '23 01:07 dbidwell94

@dbidwell94 thanks for taking the time. We'll investigate and see what we can do.

gabrieljablonski avatar Jul 02 '23 14:07 gabrieljablonski

@dbidwell94 please try the following setup:

<ReactTooltip
  id="tooltip"
  delayShow={1000}
  positionStrategy="fixed"
  style={{ zIndex: 1000 }}
/>

Setting opacity: 1 on the tooltip styling breaks funcionality. The correct way to set the opacity is by overriding the CSS variable like this:

:root {
  --rt-opacity: 1;
}

(As a side-note, we should probably add a more straight-forward way of setting the opacity, such as an opacity prop.)

I've also set positionStrategy="fixed" since the scrollbar was showing for a frame when the tooltip is inserted into the DOM. Please see if this works for you.


@knubie Please confirm if you're setting opacity: 1 manually.

gabrieljablonski avatar Jul 02 '23 15:07 gabrieljablonski

:root {
  --rt-opacity: 1;
}

Just as a heads up, I believe a recent update in the way we inject the tooltip styling into the app broke setting the opacity like this. We'll be looking into this soon.


Closing #1051 should help with this.

gabrieljablonski avatar Jul 02 '23 15:07 gabrieljablonski

@dbidwell94 please try the following setup:

<ReactTooltip
  id="tooltip"
  delayShow={1000}
  positionStrategy="fixed"
  style={{ zIndex: 1000 }}
/>

Setting opacity: 1 on the tooltip styling breaks funcionality. The correct way to set the opacity is by overriding the CSS variable like this:

:root {
  --rt-opacity: 1;
}

(As a side-note, we should probably add a more straight-forward way of setting the opacity, such as an opacity prop.)

I've also set positionStrategy="fixed" since the scrollbar was showing for a frame when the tooltip is inserted into the DOM. Please see if this works for you.

@knubie Please confirm if you're setting opacity: 1 manually.

Looks like this does indeed solve my problem. Thanks for getting back to me so quick!

dbidwell94 avatar Jul 02 '23 23:07 dbidwell94

@dbidwell94 please try the following setup:

<ReactTooltip
  id="tooltip"
  delayShow={1000}
  positionStrategy="fixed"
  style={{ zIndex: 1000 }}
/>

Setting opacity: 1 on the tooltip styling breaks funcionality. The correct way to set the opacity is by overriding the CSS variable like this:

:root {
  --rt-opacity: 1;
}

(As a side-note, we should probably add a more straight-forward way of setting the opacity, such as an opacity prop.)

I've also set positionStrategy="fixed" since the scrollbar was showing for a frame when the tooltip is inserted into the DOM. Please see if this works for you.

@knubie Please confirm if you're setting opacity: 1 manually.

I can confirm that I'm not setting opacity: 1 manually. I just updated everything in my code to the default settings and am still seeing the issue. It does appear, though, that this bug only affects one or two components, so there must be something peculiar about them that's causing this issue.

knubie avatar Jul 03 '23 01:07 knubie

Okay 😓 So i figured out the issue. It turns out that the bar under the numbers also has a tooltip, and so when I was moving the cursor out of the first tooltip anchor, i was briefly hovering over the bar, which caused it's tooltip to be displayed. So I think everything is working as intended. I will close this issue now.

knubie avatar Jul 03 '23 01:07 knubie

I'll reopen this since the original issue isn't actually fixed.

For future reference, the behavior in the first video in this issue is indeed a bug: https://user-images.githubusercontent.com/431251/230918927-995f1ccf-2449-40d8-8a4b-948c48605393.mp4

If anyone is experiencing this, refer to my earlier comment, which suggests using delayHide alongside delayShow to mitigate the issue.

If the behavior is only similar, or you're not sure, please open a new issue so it's easier to track. We'll evaluate if it should be closed as a duplicate of this.

gabrieljablonski avatar Jul 03 '23 02:07 gabrieljablonski

If anyone is experiencing this, refer to my https://github.com/ReactTooltip/react-tooltip/issues/1010#issuecomment-1501918901, which suggests using delayHide alongside delayShow to mitigate the issue.

I can reproduce the issue, but only in Firefox & Safari on a Macbook. It seems like not even
delayHide={2} and delayShow={300} will fix is for me. The only thing working is to completely remove delayShow. Looking forward to a possible fix.

Uni2K avatar Jul 11 '23 21:07 Uni2K

It seems like not even delayHide={2} and delayShow={300} will fix is for me.

@Uni2K what happens when using a slightly higher delayHide value? Please try with something like 50. The idea is there will be enough time for the mouse to reach the new anchor and trigger mouseenter before it closes, so the tooltip will simply reposition, instead of closing and showing again (which gives way to this bug).

gabrieljablonski avatar Jul 11 '23 21:07 gabrieljablonski

Okay, so we tested a lot and found out the following things:

delayShow is causing the bug. If it is very large (1000ms) the bug won't appear, only if it is like 300ms. If it is missing, the bug also does not appear. delayHide can be 0, 50, 100 and even 300, it always occurs.

This code was used to reproduce the issue:

        <div className={"h-full overflow-scroll flex"}>
            <a className={"relative top-[200px] h-[50px]   left-[100px]"}  data-for="tooltip">
                ◕‿‿◕
            </a>
          <a className={"relative top-[220px]  h-[50px]  left-[120px]"}  data-for="tooltip">
            ◕‿‿◕
          </a>
          <a className={"relative top-[600px] h-[50px]  left-[130px]"}   data-for="tooltip">
            ◕‿‿◕
          </a>
        </div>
      <Tooltip
          anchorSelect={"[data-for]"}
          delayHide={50}
          delayShow={300}
          content={"Hello World"}
      >
      </Tooltip>
    </>

See here, this is a video. We are not able to reproduce it in 100%, but maybe 80%. The tooltip is glitching during anchor change. The direction in which it is glitching corresponds to the position of the last opened tooltip.

https://github.com/ReactTooltip/react-tooltip/assets/20185223/d40cf653-b5ee-4b1e-990d-0ff4a23f13b3

See the second time the upper anchor is hovered -> the tooltip glitches from bottom to top. Also we found out that hovering from the bottom direction is causing it the most times.

I hope this helps :s

Uni2K avatar Jul 12 '23 09:07 Uni2K

Thanks for taking the time.

Another interesting thing to notice is how the opacity does not seem to transition properly (fade in on open) sometimes. I believe this is directly related. We'll investigate further.

gabrieljablonski avatar Jul 12 '23 09:07 gabrieljablonski

@Uni2K I've tried many different ways but I'm unable to reproduce this. Are you able to provide a sample project?

gabrieljablonski avatar Jul 14 '23 15:07 gabrieljablonski

@gabrieljablonski This glitch was only happening on Safari & Firefox on a Macbook. Never on Chrome, never on windows. I am in vacation and don't have a Macbook here, so I cannot create a sample project which reproduces the bug. The fastest time I could provide you a complete sample project is on 20.07.

At least I can tell you that we just used a standart next.js template. The code above was just used as direct "return" of a page route. We tried a lot before we could find a somewhat reproduceable situation. Even with that, the glitch only happens in about 80% of the time. So, there has to be a random factor that to it.

Uni2K avatar Jul 14 '23 20:07 Uni2K

@Uni2K I do have a Macbook, please create a reproducible example and I'll try to test and debug it from my side, I'll share my findings with @gabrieljablonski and we will try to fix it if possible.

danielbarion avatar Jul 15 '23 18:07 danielbarion

Here is the repo: https://codesandbox.io/s/amazing-sanne-lnwmfg

My coworker tested it with his Macbook. He could not reproduce the issue in Firefox, but he could reproduce it in Safari 16.3 on every 5-7 attempt. For now, this is the best I can provide.

Uni2K avatar Jul 16 '23 18:07 Uni2K

I tried a lot of times but the issue only happened 1 time with me, this will be hard to debug and fix.

I'll take a look if we can reset the Tooltip coordinates when we close it, or something like that.

danielbarion avatar Jul 20 '23 10:07 danielbarion

@danielbarion we could try, but I'm guessing it won't work.

my bet is on some weird CSS specification that safari doesn't follow exactly as it should. maybe something to so with how opacity transition is behaving

gabrieljablonski avatar Jul 20 '23 10:07 gabrieljablonski

Maybe I can help with a new video, checkout the clicking:

https://github.com/ReactTooltip/react-tooltip/assets/20185223/dad094f0-a6cc-42cb-9974-995a50b1bc2d

Uni2K avatar Jul 31 '23 10:07 Uni2K

Perhaps it is caused by the same reason.

When their contents are the same, moving from an element that is set not to display tooltip to its neighbor element that displays tooltip, it will appear misaligned.

image image

When their contents are different, there will be the flickering phenomenon that others have mentioned. image

lopo12123 avatar Nov 22 '23 10:11 lopo12123

I was having this issue (or at least a very similar one, mine was always only flickering in the top left of the page), and this was the hacky solution I came up with to get at least something working using the afterShow/afterHide props to delay the showing until after the flickering was over, in case it helps anyone else:

const [classNameSuffix, setClassNameSuffix] = useState('hidden');
return <Tooltip
          id={uid}
          className={`tooltip ${classNameSuffix}`}
          delayHide={50}
          delayShow={400}
          clickable
          afterShow={() => setTimeout(() => setClassNameSuffix('visible'), 50)}
          afterHide={() => setClassNameSuffix('hidden')}
        >
          {....}
</Tooltip>

and then my scss is just:

.tooltip {
  &.hidden{
    visibility: hidden;
  }

  &.visible{
    visibility: visible;
  }
}

StevenSawtelle avatar Mar 26 '24 20:03 StevenSawtelle