primereact
primereact copied to clipboard
Menu: Popup menu positioning - appendTo
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
isself
and user scrolls first, then clicksMenu
, theMenu
appears as far below the containing element as the user has scrolled. - If
appendTo
is not set,Menu
is attached to documentbody
(theappendTo
default), so when the user scrolls theMenu
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
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.
Thanks for posting your hack at least! 😄
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 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?
appendTo="self"
Worked like a charm for me. Legend! Thank you :)
@gnowland as with PrimeNG I think the solution is to
appendTo
thebody
.Here is the top calculated correctly for both
self
andbody
.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 callsDomHandler.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.