primereact icon indicating copy to clipboard operation
primereact copied to clipboard

Menu: Popup menu positioning - appendTo

Open gnowland opened this issue 2 years ago • 3 comments

Describe the bug

Same issue as https://github.com/primefaces/primeng/issues/1054. When Menu property popup is enabled and Menu is in a fixed element (e.g. a header with css position: sticky) menu positioning is haywire.

  • When appendTo is self and user scrolls first, then clicks Menu, the Menu appears as far below the containing element as the user has scrolled.
  • If appendTo is not set, Menu is attached to document body (the appendTo default), so when the user scrolls the Menu scrolls with the body and loses its position. (expected behavior?)

Reproducer

https://codesandbox.io/s/primereact-test-forked-1dlh7o?file=/src/index.js

PrimeReact version

8.1.1

React version

17.x

Language

TypeScript

Build / Runtime

Create React App (CRA)

Browser(s)

No response

Steps to reproduce the behavior

See codesandbox example and description details for steps to reproduce.

Expected behavior

The menu to appear consistently below the sticky containing element regardless of scroll

gnowland avatar Jun 09 '22 17:06 gnowland

I've temporarily hacked a patch where I wrap the button and menu element in a div, set to position: relative and fixed height, set Menu appendTo="self" and then override the Menu positioning thus:

.menu-container {
  position: relative;
  height: 40px;
}

.p-menu {
  top: 0 !important;
  left: 0 !important;
  margin-top: 40px;
}

Obviously this is a hack, not a long-term solution.

gnowland avatar Jun 09 '22 20:06 gnowland

Thanks for posting your hack at least! 😄

melloware avatar Jun 09 '22 20:06 melloware

No prob! It should be an easy fix, likely JS is measuring offset position of "self" element from document top instead of window. If I have time I'll make a PR, but I'm pretty overwhelmed with work right now so don't hold your breath... 😩

gnowland avatar Jun 09 '22 20:06 gnowland

@gnowland as with PrimeNG I think the solution is to appendTo the body.

Here is the top calculated correctly for both self and body.

targetOuterHeight = 42
targetOffset.top = 107.75 
windowScrollTop = 1128

targetOuterHeight = 42 
targetOffset.top = 101.75
windowScrollTop = 1128

I think its always calculating from the top of the body when it calls DomHandler.absolutePosition(menuRef.current, targetRef.current);.

melloware avatar Dec 07 '22 13:12 melloware

I think by you adding a container element that is relative and setting top left to 0px you are essentially doing all that work instead of just using body no?

melloware avatar Dec 07 '22 14:12 melloware

appendTo="self"

Worked like a charm for me. Legend! Thank you :)

gnagakarthik avatar Dec 15 '23 23:12 gnagakarthik

@gnowland as with PrimeNG I think the solution is to appendTo the body.

Here is the top calculated correctly for both self and body.

targetOuterHeight = 42
targetOffset.top = 107.75 
windowScrollTop = 1128

targetOuterHeight = 42 
targetOffset.top = 101.75
windowScrollTop = 1128

I think its always calculating from the top of the body when it calls DomHandler.absolutePosition(menuRef.current, targetRef.current);.

I think by you adding a container element that is relative and setting top left to 0px you are essentially doing all that work instead of just using body no?

I still don't think this solves the issue. As @gnowland originally mentioned, the issue is when the menu is attached to an element with fixed position, like a navigation bar which stays at the top of the page.

Here is my basic understanding of how the primereact popups work. When the popup is first rendered, it is given an absolute position. Values for top, left, bottom and right are computed relative to the body so that the popup renders right below the element which fired the toggle event. Let us call this the triggering element.

This works nicely with scrolling: if the menu is toggled open and the user scrolls down the page, then the menu and its triggering element will "scroll off" together.

However, there are issues when the triggering element is inside of a fixed element, e.g. a navigation bar which stays at the top of a page. In this case, when the user scrolls down the page, the menu (positioned absolutely, relative to the body) will "scroll off" even while the triggering element stays fixed, thereby causing the menu to be separated from its triggering element.

You might then suggest setting appendTo = 'self' instead of the default appendTo = document.body. However, this still does not work properly, because the absolute position of the popup is always computed relative to body, even when we would like for it to be computed relative to the fixed navigation bar. You will notice the issue when you scroll down the page a bit, and then open the menu via the triggering element in the navigation bar. The popup will appear as far below the navigation bar as you have scrolled.

I would therefore recommend that this issue be reopened.

peconomou929 avatar Apr 02 '24 12:04 peconomou929