Popping Out of Hidden Overflow
The following is a guest post by Agop Shirinian. Agop ran into an interesting scenario where he needed an element to be scrollable in one direction, while allowing the overflow in the other direction. You’d think that’s what overflow-x and overflow-y are for, but it’s not that simple. I’ll let Agop explain. So you’re tasked with creating a scrollable menu with submenus that pop out when you hover over a parent menu item. Simple! Create a list for the menu, add some nested lists for the submenus, position the nested lists based on their parent list items, voilà! See the Pen Scrollable menu with pop out submenus (broken) by Agop (@agop) on CodePen. Wait, that’s not right. Oh, of course, we used overflow: auto – perhaps if we use overflow-x: visible , the horizontal overflow of the submenus will be visible: See the Pen Scrollable menu with pop out submenus (broken #2) by Agop (@agop) on CodePen. What gives? Why do we still get scrollbars?
The computed values of ‘overflow-x’ and ‘overflow-y’ are the same as their specified values, except that some combinations with ‘visible’ are not possible: if one is specified as ‘visible’ and the other is ‘scroll’ or ‘auto’, then ‘visible’ is set to ‘auto’.
overflow-x: visible; overflow-y: auto;
overflow-x: auto; overflow-y: auto;
So we can’t have visible horizontal overflow if the vertical overflow is invisible, and vice versa. And if we can’t have visible horizontal overflow, we can’t have our pop out submenus!
Interestingly enough, if we omit the position: relative from the menu items, the submenus do show up, positioned based on their closest positioned ancestor. In this case, they don’t have a positioned ancestor, so they’re positioned relative to : See the Pen Scrollable menu with pop out submenus (step 1) by Agop (@agop) on CodePen. Basically, in order for an absolutely positioned element to appear outside of an element with overflow: hidden , its closest positioned ancestor must also be an ancestor of the element with overflow: hidden . Knowing this, we can add a wrapper around the menus to act as the closest positioned ancestor for each submenu. Then, whenever the user hovers over a menu item, we can position the submenu wrappers using a bit of JavaScript: See the Pen Scrollable menu with pop out submenus by Agop (@agop) on CodePen. And that’s it! Since neither the menus nor the menu items are positioned, the submenus are able to pop out of the hidden/scrollable overflow. Now we can have as many levels of nested submenus as we want, and we won’t get any undesired clipping.
Unfortunately, this method of showing items that would otherwise be hidden is very obscure. It’d be nice if we could specify a clip depth, which would control which ancestor in the hiearchy would be responsible for clipping a particular element:
./* Fair warning: not real code */ .submenu < /* only an ancestor 2 levels up can clip this element */ clip-depth: 2; >
/* Fair warning: not real code */ .submenu < /* only an ancestor that matches the .panel selector can clip this element */ clip-parent: .panel; >
Psst! Create a DigitalOcean account and get $200 in free credit for cloud-based hosting and services.
Comments
Thanks for the post. I was just working on a similar problem a couple hours ago. I will try this tomorrow to work out a fix
I swear, the specs for overflow-x/y are among the most idiotic ever. What’s the unearthly reason to forbid having simple combinations like visible/auto?
Just don’t tell me it’s because it’s complicated…
It’s likely because the specificiation paves the cowpath; following the original implementation from Microsoft in IE 5. Altering the behavior would break that legacy compatibility.
Thank you for the effort, it was well-written. The real “Takeaway” is that in 2014 we still can not use state-of-the art html/css to create menus which were state of the art in 2006. Proceeding beyond 2006, this example does not look great on a small mobile device. I get blinky/flashy artifacts on a modern iphone for example. I am aware of the standard excuses, no need to trot them out. However, this should not end up in a modern web app without caveats (“a desktop-preferred technique to…”). If I’m wrong, please point me to the live, production site which uses this technique. We can’t sweep mobile under the rug in 2014, and it’s not clear how to fix your approach on a mobile device. If we can’t discuss the elephant in the room, we should at least acknowledge it 🙂 thanks again!
You’re right, I wouldn’t use this technique on a mobile device. ..or anything at all without a decently large screen and a persistent cursor to hover over things 😉 This technique mainly aids desktop applications with long menus and lists with popups to reveal additional information about those items.
standard-body, I actually had to use this technique, which I stumbled upon for our internal application. Yes this is a desktop oriented app and for mobile would not use it, mainly because mobile UI/UEX requires a different set of requirements. Yeah position is great except when it isn’t. Couple this with visibility and you can pull your hair out in complex interactions.
Great posts. I come into this on almost every project nowadays. Mobile menu design pattern seems to have it’s quirks that need to be ironed out. I will be keeping this in mind for my next build when I undoubtedly run into this again.
Position: fixed; is also able to get outside any overflow: hidden; element since it only positions to the viewport and so also only accepts the viewport overflow.
Great post. But overflow doesn’t work well in mobile devices. Is there a solution except for using third-party plugins?
I’m not entirely sure this approach would make for a good mobile experience anyway, so you would probably want to engage in some creative progressive enhancement. With several levels of menus flying out, this seems best suited for wider screens.
Woaw, this is a good one ! For those, like me, who have to deal with IE7 every day : you know what ? It even works on IE7 ! (JSFiddle) Thanks a lot for this trick !! 🙂
Speaking of coincidences! I was struggling on this exact problem yesterday for a mobile sliding navigation I was tasked to build! This works beautifully on all devices.
why the .related-articles-title , .comments-title , .comments-title have margin-bottom: -7px ?
it was blocked.
Almost forgot – You needn’t Wrap the Top Tier Items in the Div Wrapper either, incase you want to position them inline or otherwise 🙂
PURE CSS Solution. Hi there, I have achieved the same solution using pure css – no need for the JS to position the subs correctly 🙂 Use the following for your css – and of course play around and modify the attributes to suit your needs:
.wrapper < position: relative; background-color: transparent; padding-left:138px; margin-top:-23px; width:150px; height:0px; >ul < list-style:none; width: 200px; max-height: 250px; overflow-x: hidden; overflow-y: auto; >li < padding-left:5px; padding-top:3px; position: static; height:30px; >li .wrapper < position: absolute; z-index: 10; display: none; cursor: auto; >li:hover > .wrapper < display: block; >li:nth-child(2n) < background: #0e8ce0; >li:nth-child(2n+1) < background: #0064b3; >li.parent
I attempted to use the recommended “solution” technique, but found it not useful as I couldn’t solve the problem of getting the submenu to be positioned according to its parent menu item. I must use position:absolute on the submenu in order to get it display outside the bounds of the scrollable list. To preserve the vertical position of the submenu (attached to the menu item), I use top:auto. However, this seems to cause the browser to incorrectly position the submenu. In Chrome, the submenu is positioned correctly when the menu is not scrolled, however when the menu is scrolled the browser seems not to recalculate the vertical position, and the submenu is displayed where the menu list item was prior to scrolling. i.e. scroll has no effect on the vertical position of the submenu in Chrome. In Firefox it seems the vertical position is recalculated when the item is redisplayed from display:none (however the same bug can be seen if hover is maintained during scrol l). Unfortunately these two problems (the problem described in the blog post, combined with the problem described here) have forced me to abandon a CSS solution to this problem. I had really hoped to avoid a JS solution.
I see what John means about scrolling the parent menu and not seeing the child menu move along with it. The solution presented here is already a JS solution though, and so more JS could fix it (scroll event handler, reposition submenu).
Keep up to date on web dev
with our hand-crafted newsletter
DigitalOcean
Css overflow hidden position fixed
If you have an element with position: fixed inside of an element that has overflow: hidden , what’s the expected rendering when you need to, uh, overflow? Should the inner fixpos element be clipped by its parent or not?
Fixed positioning is similar to absolute positioning. The only difference is that for a fixed positioned box, the containing block is established by the viewport.
So, according that spec text, the parent element’s overflow shouldn’t have any effect because the fixpos’ parent element is the viewport.
Neat. But how do browsers behave? Open this testcase and have a look:
Everything behaves as the spec describes. overflow: hidden on the parent is ignored. High-fives all around.
Now if you throw in both a z-index: 1 (any number will do) and a position: relative on the parent element, things get…different.
Same as first testcase (what I would expect): Mobile + Desktop Firefox Mobile Chrome Desktop Edge
Different from first testcase: Mobile + Desktop Safari: fixpos element is clipped by parent (meaning overflow: hidden worked). Mobile + Desktop Opera (Presto): same as Safari Desktop Chrome: if the viewport is smaller than the containing parent, then overflow: hidden on the parent kicks in (resize the browser window to see it). Mobile + Desktop Opera (Blink): same as Chrome
And now, a 3rd testcase which adds user-scalable=no to the meta (viewport) element (the same effect happens if you constrain intial-scale and maximum-scale to 1.
The only browser this seems to make a difference in is Chrome on Android, which now clips the child element. I think I discovered this on accident.
So it seems like non-Edge and non-Firefox browsers treat a position: fixed element as a position: absolute element (or something?), when contained by a position: relative parent that also has a z-index set.
Unfortunately at least one site relies on this bug (see this comment).
If you happen to know why, send a self-addressed stamped envelope to twitter dot com slash miketaylr and let me know.
See https://twitter.com/gregwhitworth/status/609095284010852352 from @gregwhitworth. I’ll try to write a follow-up post when I understand everything.
© 3000. Mike Taylr Dot Com Web-Ring. File a bug on this blog.
Saved searches
Use saved searches to filter your results more quickly
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
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
position: fixed + overflow:hidden causing issue #74
position: fixed + overflow:hidden causing issue #74
Comments
With the recent changes in PR #68 where position: fixed was added alongside the calculation to negative margin (top) of the page to prevent jumping.
The issue I’m facing is with position: fixed where on browser. This would happen.
As you can see the element which i’m highlighting doesn’t take the full width of the page unless I add left: 0; right: 0; to the class .tingle-enabled
Secondly, with the existing overflow:hidden rule set with the new changes to adding top: -(margin) the content outside of the body’s height will get clipped off as I’ve illustrated in my screenshot.
after modal opens. the content dissappears.
I understand it is because of overflow: hidden because if I switch it to overflow: visible it would work. but may cause problems that I’ve yet to run into.
Also I know this issues doesn’t occur on the demo site so I’m just wondering if there’s a way around this. Cheers!
The text was updated successfully, but these errors were encountered: