Skip to content

[BUG] Tooltip position not updated on scroll #1044

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Uni2K opened this issue Jun 20, 2023 · 16 comments · Fixed by #1045
Closed

[BUG] Tooltip position not updated on scroll #1044

Uni2K opened this issue Jun 20, 2023 · 16 comments · Fixed by #1045
Labels

Comments

@Uni2K
Copy link

Uni2K commented Jun 20, 2023

Describe the bug
Using a minimal setup like:

  <div className={"h-full overflow-scroll flex"}>
         <a className={"relative top-1/2"} data-tooltip-id="my-tooltip" data-tooltip-content="Hello world!">
                ◕‿‿◕
           </a>
        </div>
  <Tooltip id="my-tooltip" closeOnScroll={true}/>

The position of the tooltip is not updated during scrolling. The problem can be seen here:
tooltip

The solution is to remove either "overflow:scroll" or "display:flex" from the parent div of the anchor element.
If both are present, the bug will occur. It also does not make a difference where the Tooltip is placed (in which div/DOM element), as long as the anchor parent has both CSS properties, the bug will occur.

Version of Package
v5.14.0

To Reproduce
Use the code above.

Expected behavior
´The tooltip disappears (closeOnScroll==true) or at least it updates the position to follow the anchor element.

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

  • OS: Win
  • Browser: Chrome
  • Frameworks: Next.js latest stable
@Uni2K Uni2K added the Bug label Jun 20, 2023
@gabrieljablonski
Copy link
Member

gabrieljablonski commented Jun 20, 2023

This is not a bug.

Updating the tooltip position while scrolling is not viable, so you must adjust your CSS/DOM structure accordingly.

I'm not too sure how you'd go about it in your specific case, but I'd start with trying position: relative on the parent for the tooltip component, or with a wrapper between your anchor and its scrolling parent.

Maybe we should add a more in-depth explanation in our documentation/troubleshooting section about this, but that's pretty much it.

@danielbarion please share your thoughts, or close this as "not planned" if you agree.

@Uni2K
Copy link
Author

Uni2K commented Jun 20, 2023

Okay, it feels like a bug, because updating the tooltip position works for many cases perfectly. But just not with this CSS combination.
Could you give me some more information on what I should try? I have no understanding on what the problem exactly is.

with a wrapper between your anchor and its scrolling parent

So that the direct parent of the anchor is not scrollable?

@gabrieljablonski
Copy link
Member

updating the tooltip position works for many cases perfectly

In those cases, technically the tooltip position isn't changing, it's just retaining its position relative to the scrolling element.

The issue arises when the anchor is placed inside a scrolling element, and the tooltip component is outside of it, so the reference moves and the tooltip doesn't. The position doesn't get recalculated because it would be too expensive to do so.

Having thought a little more about this, I believe placing the tooltip component inside the scrolling element should be good enough. If it doesn't work, please let us know.

@Uni2K
Copy link
Author

Uni2K commented Jun 20, 2023

Having thought a little more about this, I believe placing the tooltip component inside the scrolling element should be good enough. If it doesn't work, please let us know.

It works for the vertical position, but the "overflow:scroll" of the parent container will mess up the placement of the tooltip (e.g. place="left" won't work).

My entire goal was to close the tooltip on scroll, not updating its position. The 'closeOnScroll' however, does not work in this case, because the tooltip itself does not detect a scroll. Since placing it inside the scrollable container messes up the placement, the solution would be to detect general scroll events and manually close the tooltip.

The drawback is that the wrong position is visible for the time of an animation or "delayHide". So both values need to be as small as possible.

The only possible solution for this library that I can imagine is to include a prop called forcePlace, which makes sure that the desiredPlace is always the one specified. In this way the tooltip could be included in the scrollable container and the scrolling problems would be fixed. But maybe it is too much an edge case.

Anyway, I think you can close this issue. Thanks for taking the time :)

@danielbarion
Copy link
Member

Hi guys,

@Uni2K you can try the approach of writing the logic to close the tooltip from your side using a controlled state for the tooltip and add an event listener to switch the state when the scroll event happens.

Thank you both for the discussion, I believe this will help other people with the same question.

I'll close this ticket, but feel free to comment or create a new issue if you need.

@danielbarion danielbarion closed this as not planned Won't fix, can't repro, duplicate, stale Jun 20, 2023
@gabrieljablonski
Copy link
Member

The 'closeOnScroll' however, does not work in this case, because the tooltip itself does not detect a scroll.

I wasn't aware this was the case. The intended behavior was to close when scrolling anywhere on the page. I'll have a look on this.

The drawback is that the wrong position is visible for the time of an animation or "delayHide".

When closing from scrolling, the tooltip should probably ignore delayHide.

I'll reopen since it does seem to be a bug with closeOnScroll.

@gabrieljablonski
Copy link
Member

gabrieljablonski commented Jun 21, 2023

@Uni2K We should be merging it soon, but until then you can check out [email protected].

closeOnScroll works a bit better now, and we added a troubleshooting section on how to get the tooltip to be positioned correctly when inside a scrolling element. Until this is available directly on the official docs, you can have a look here.

@Uni2K
Copy link
Author

Uni2K commented Jun 22, 2023

Tested it out, works perfectly when the tooltip is inside the scrolling container.

If the tooltip is not inside the scrolling container, there seems to be a small threshold on which the tooltip won't be closed or at least closes significantly later. This differs from the usual behaviour, where the tooltip closes immediately on any scroll. See here, I am below that threshold:
tooltip
Other than that it seems to work correctly :)


Other thing:
From my post above:

Since placing it inside the scrollable container messes up the placement, the solution would be to detect general scroll events and manually close the tooltip.

It tested some things and I made it reproduceable:

<div className="flex min-h-[100vh] flex-row-reverse ">
            <div
                className="relative basis-1/4  overflow-scroll"
            >
                <a className={"relative top-20"} data-tooltip-id="my-tooltip" data-tooltip-content="Hello world!">
                    ◕‿‿◕
                </a>
                <Tooltip id="my-tooltip" place={"left"} delayHide={2000}/>
            </div>
        </div>

image

The issue here is that the "place" prop is ignored if "overflow-scroll" is present on the parent of the anchor. Is this something that you are aware of?

@gabrieljablonski
Copy link
Member

gabrieljablonski commented Jun 22, 2023

If the tooltip is not inside the scrolling container, there seems to be a small threshold on which the tooltip won't be closed or at least closes significantly later.

As mentioned on my last comment, we are soon to add a section on our troubleshooting regarding this.

For the tooltip to close when using closeOnScroll, the scrolling element must be either the root html tag, the direct parent of the tooltip component, or the direct parent of the anchor element. My guess is that on your example, the anchor element isn't a direct child of the scrolling element.

Since placing it inside the scrollable container messes up the placement

This is by design. floating-ui (which is what we use behind the scenes) offers "middlewares" to change how the tooltip gets placed on the page. More details here.

This is how you'd setup the tooltip to work with default behavior:

import { offset, flip, shift } from '@floating-ui/dom'

function MyComponent() {
  return (
    <div>
      ...
      <Tooltip
        offset={10} // just for clarity for where `offset()` gets its parameter internally
        middlewares={[offset(10), flip(), shift({ padding: 5 })]}
      />
    </div>
  )
}

flip() and shift() are used to make sure the tooltip remain visible if it would overflow its parent. offset() moves the tooltip away from the anchor element.

On your case, try leaving just the offset (with whatever value you prefer, 10 is the default).

import { offset } from '@floating-ui/dom'

<Tooltip
  middlewares={[offset(10)]}
/>

@Uni2K
Copy link
Author

Uni2K commented Jun 22, 2023

As mentioned on my last comment, we are soon to add a section on our troubleshooting regarding this.

You are referring to the "caution" section?
I think I fullfill the conditions, the setup is like:

<div className="overflow-scroll"> //scrolling element
<div id="element1"></div>
<div id="element2"><div id="anchor"></div></div>
<div id="element3"></div>

<Tooltip>
</div>

So, the scrolling element is the parent of the Tooltip, the anchor is nested inside other divs. Do I understand it correctly?


This is by design. floating-ui (which is what we use behind the scenes) offers "middlewares" to change how the tooltip gets placed on the page. More details here.

Just removing the flip would give a perfect result, but the tooltip is cut off at the left side. Which is, again, just due to the "overflow-scroll".
image

After reading a bit in the floating-ui github and maybe related issues (floating-ui/floating-ui#112), I fear that there is just not a solution for this case. The overflow-scroll will always prevent the tooltip from being drawn correctly. :s

@gabrieljablonski
Copy link
Member

gabrieljablonski commented Jun 22, 2023

After reading a bit in the floating-ui github and maybe related issues (floating-ui/floating-ui#112), I fear that there is just not a solution for this case. The overflow-scroll will always prevent the tooltip from being drawn correctly. :s

That does seem like it would be the case. Until we can think of a way around this, closing the tooltip is probably the best alternative in cases like this.


I've just realized that in several responses in this thread (fixed now), I referred to closeOnEsc, when I obviously meant closeOnScroll. Please check your code if you have the correct prop set, wouldn't be surprised if I confused you, since I spent the last 15m trying to figure out why it had stopped working when it worked fine yesterday. I had set closeOnEsc 🤦‍♀️

Here's a minimal working example. https://stackblitz.com/edit/stackblitz-starters-9exiwk?file=src%2FApp.tsx

@Uni2K
Copy link
Author

Uni2K commented Jun 22, 2023

Fortunately, I only used the correct closeOnScroll prop :)
But I found out I may understood your new documentation wrong. Turns out I had not placed the tooltip inside the scroll element, but a layer outside.

This example showcases my situation at that point:
https://stackblitz.com/edit/stackblitz-starters-b1xmlv?file=src%2FApp.tsx

-> The solution would be to place the tooltip INSIDE the container. And when I do it, the tooltip is cut off (thats what I wrote above). Here is the example using your code:
https://stackblitz.com/edit/stackblitz-starters-3dhbwt?file=src%2FApp.tsx

Any more suggestions? :s

@gabrieljablonski
Copy link
Member

gabrieljablonski commented Jun 22, 2023

My final suggestion would be to have the tooltip outside the scrolling element (so it doesn't clip), and have the anchor elements as direct children to the scrolling element (so closeOnScroll works). Here's the example updated: https://stackblitz.com/edit/stackblitz-starters-jpogxw?file=src%2FApp.tsx

The only big obstacle I can imagine this would be for you is that it might not be trivial to have the anchor elements as direct children of the scrolling element.

If that's the case, there's not much else I can think of, unless you'd be willing to have your own version of react-tooltip with a few tweaks, such as attaching the closeOnScroll listener to the parent of the parent and so on.

@Uni2K
Copy link
Author

Uni2K commented Jun 22, 2023

Unfortunately, I use a more complex layout which does not allow the anchors to be the direct children. It would just kill the UX.

Anyway, @gabrieljablonski thank you very much for your time and for the tweaking of closeOnScroll. The beta version works to a good extent. I think I can leave it as it is right now :)

@gabrieljablonski
Copy link
Member

I've realized the "direct child" restriction was just me being extremely lazy. Try out [email protected], it should allow you to use closeOnScroll regardless of the DOM structure.

Your first example with the package updated: https://stackblitz.com/edit/stackblitz-starters-dxxsod?file=src%2FApp.tsx

@gabrieljablonski
Copy link
Member

Available on official release [email protected]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants