react-select: positioning the component in containers that have `overflow: scroll;`
I recently ran into this issue when using react-select components in a modal. For mobile the contents of the modal are scrollable. In this particular case, the Select was at the bottom of the content. Activating the Select made the container overflow and scrollable.
Maybe the container notion of http://react-bootstrap.github.io/react-overlays/examples/ might make sense?
Alternatively, maybe make use of https://github.com/souporserious/react-tether?
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 45
- Comments: 64 (2 by maintainers)
one more use case when dropdown should flow above all elements and should not be restricted by container’s size
Did you try using menuPosition=“fixed” like that:
<ReactSelect {...props} menuPosition="fixed" />?If you are using react 16+ and you’re not willing to add new dependencies to your project (react-tether and react-dimensions), you could use react portals to achieve the same behavior.
@andreifg1 menuPosition=“fixed” did the trick on v3 👍
I ran into the same problem as @burtyish. (before actually reading his comment) I found a fix that works for the regular react-select (haven’t tested creatable/async) that doesn’t use
react-dimensionsNow your component doesn’t need to be the full width, and has one less dependency.
@maxmatthews you can try the following, it worked for me.
1 - Comment out the rule
/*top:100px*/from the original css class.Select-menu-outer2 - Add the following custom css to your stylesheet:3 - Add the class .menu-outer-top to the Select to manually change to position of the dropdown (See screenshot below)
Example:
Final results:
Hope it helps!
Really important feature.
There are plenty of situations where you you don’t want the drop-down to cause overflow. Actually, it should be the default behavior, since that’s how the native select box work.
Actually, I was able to shorten this a good bit using react-tether and react-dimensions:
For v2 solution check #2439
Thanks @oluckyman for the great solution. I’ve modified it a bit to work with react-select:
TetheredSelect component that overrides Select’s menu rendering:
(Btw, does anybody know, why renderOuter can’t be directly overridden here?)
TetherComponent:
My case is pretty complex. So here are only important parts. In my Dropdown component
renderI do:And here are parts of DropdownMenu component:
and in
renderI return:And here is TetherComponent as is (just a wrapper around tether lib):
@kamagatos I think it’d be great if you could make a pull request to integrate React Portals into this project. It’ll be very useful.
@kamagatos Thank you for this! Made my life so much easier.
One note, I was using this in a page that is scrollable and I had to make the following addition when setting the top so that it worked when scrolling:
top: dimensions.top + dimensions.height + window.pageYOffset
I agree that it would be great if this were somehow baked into react-select. Thanks again!
In case anyone needs it, here’s a hacked together TypeScript version based on the post by @burtyish. I think I fixed the mobile issues, see the
_handleTouchOutsidemethod, it needs to check that the touch event was inside the tethered item (which is not inside the react-select wrapper). It does this by overriding the react-selecthandleTouchOutsidemethod. I also switched to react-measure for measurements, since I use that elsewhere in my project.Thanks for everyone sharing here. Since I found this thread useful, I want to share my experience on using
Dimensionshere. Wrapping the entire Select withDimensions, as in https://github.com/JedWatson/react-select/issues/810#issuecomment-250274937, results in the entire component being wrapped in a div styled bytether.jsthus:I found this can create a problem, for instance if the Select has a
floatstyle.I solved this by getting
Dimensionsto wrap just Select’s outer element. Here’s my tweak. Note: I solved it for regular, notCreatableSelect.Is anyone currently working on adding this either as default behaviour or as an option? @stinoga’s solution seems to work well.
@oluckyman Having the same issue here! If I have other elements below the Select, I have no issues since there is plenty of space and I also reduce the height of the dropdown, but when the select is closer to the bottom of the page it pushed the main container.
Were you able to find a solution for this?
I ended up using
dropdownComponentprop with my own wrapper component around the menu component. That wrapper is using ‘portal’ technic to render menu intobodynode. Also I’ve used tether lib to position menu. Pretty happy with this solution. Bonus: now the menu drops up if there is no place below the input.menuPortalTargetis decent, but in a fixed scrollable modal, in some cases it does not work well with scrolling (i.e. if you have anotherfixedelement below the scrollable area). You can usecloseMenuOnScroll, but it is quite visibly not closing the menu quickly enough.For now, I have manually overriden
react-select’s menu placing logic by manually calculatingmenuPlacementprop value and passing it toSelect. The problem is thatSelectcontains some state inside of it, so you have to fully rerenderSelectwhen menu placement changes (you do this by passing a newkeyprop).As mentioned by @crohn, I think it should be considered to allow explicitly overriding the menu placement logic.
There is still one problem with the @kamagatos Portal solution: when you open your menu on a page with a lot of content(with browser scroll bar) and then we scroll with our browser, the menu stays open and floating. Anyone managed to close the react-select menu on browser scrolling?
@kamagatos using Portals is a way to go! The only problem with your example is that when the page is scrolled down, the menu will have the wrong top position:
top: dimensions.top + dimensions.height,in your example. You should also add a vertical scroll value like thistop: dimensions.top + dimensions.height + window.scrollYso when your page is scrolled down before opening select - the outer menu will appear as it should - below your field. If your page could have a horizontal scroll, just think about that also.@tutok i’ve not dealt with the touch support issue in my application using @nehero’s solution. but this is probably the way? see. the _handleTouchOutside solution from @russpowers’s comment https://github.com/JedWatson/react-select/issues/810#issuecomment-263863746
Here’s an ES6 implementation of @russpowers’ TypeScript component if anyone wants it:
And here’s the same handleClickOutside function applied to @stinoga’s component for Async/Createable
These both seem to work on desktop and mobile for me.
Would be good to find easier solution to this problem, anybody has ideas where to look?
To me this issue gets worse when using
Async(even not in mobile), I think because the height of the menu changes after options get loaded.Is there a way to trigger positioning computation manually? As far as I understand,
MenuPlacerpasses a ref callback toMenuin whichgetPlacementmagic happens, updatingMenuPlacerstate. So I guess the answer is no.Do you think exposing an API to trigger positioning computation could be useful in future releases? Something like the
focusorbluryou already expose.Answering my own questions. It seems that in the
Selectcomponent the following function will close the menu every time on touch devices:My guess is that because with both the
react-tetherand theReactDOM.createPortalsolutions the actual menu element is rendered directly to thebodyelement in the dom, thethis.wrapper.contains(event.target)call always return false.To fix this I did override the
handleTouchOutsidefunction to also look whether the touch is inside the menu and menuContainer elements. Here is the updated versionWith my quick testing this seemed to work. However I don’t know if this has any unwanted side effects
Thanks @nehero your solution saved me a lot of work! 👍 I want to marry you hahaha
I found a css solution without using Portals. In your Select wrapper component capture and handle the onOpen function of the Select component to repositionate the container with fixed position based on the Select-control div. Probably not the best solution, but it’s simple and it works. Hope it helps
@stinoga No, just copy the
Select.AsyncCreatableto your project with replaced importimport Select from './PathToYourTetheredSelectFromTheAbove';@juan0087 WOW! Thanks for that detailed response. Will give it a shot right now, and I’m sure it will help others in the future.
Has anyone had any luck with this? I can’t use a react-select in a modal without it causing scrolling issues. If I try and hijack
.Select-menu-outerand set it toposition: fixedI get weird scrolling issues. Someone suggested using react-tether, but it doesn’t look like that’s an easy implementation.