Skip to content
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

[css3 positioning] support position:sticky inside an overflow:hidden|auto on general parents #865

Open
hunboy opened this issue Jan 6, 2017 · 82 comments
Labels
css-position-3 Current Work

Comments

@hunboy
Copy link

hunboy commented Jan 6, 2017

css3 position

Currently the position:sticky element exclusively works when the all of general parents are overflow:visible. This is problematic a little whilst we use overflow:hidden trick for clearfix etc.

Testcase attached. Here is a simplesample, how is the layout broken when we change the overflow to visible, however sticky starts to work in that case. I detected, all of general parent-elements in the parent-path must be overflow: visible, which is weird a little. Any overflow:hidden in the parent-path kills the sticky.

ER: sync somehow the spec to allow sticky subcontainers inside a overflow:hidden general parent.

testcase: https://jsfiddle.net/utasir/rmmkxq62/11/

@hunboy
Copy link
Author

hunboy commented Jan 6, 2017

mozbugzilla link : https://bugzilla.mozilla.org/show_bug.cgi?id=1329203

@hunboy hunboy changed the title support position:sticky inside an overflow:hidden|auto on general parents [css3 positioning] support position:sticky inside an overflow:hidden|auto on general parents Jan 6, 2017
@dauwhe dauwhe added the css-position-3 Current Work label Jan 9, 2017
@pedi
Copy link

pedi commented Mar 31, 2017

maybe not really a bug but by implementation. Check this specs out

http://www.coreyford.name/files/position-sticky-presentation/

The box’s position depends on its containing block (established as for position:static) as well as its scrolling container, defined by the nearest ancestor in the same document with a computed value for ‘overflow-x’ or ‘overflow-y’ other than ‘visible’, or the viewport if no such ancestor exists.

@niutech
Copy link

niutech commented Aug 23, 2017

The official spec for position: sticky is at W3C and there is no mention of overflow: hidden. I do not see a reason why an element with position: sticky cannot be sticky inside <body> with overflow-x: hidden. Please fix it.

@mstange
Copy link

mstange commented Aug 23, 2017

This spec seems to have changed quite a bit since the implementation in Gecko. It no longer mentions scrolling ancestors, it exclusively talks about containing blocks now. I agree that the current writing of the spec would allow crossing overflow:hidden / auto descendants when computing the offset. But I don't know what the rationale behind that change to the spec was, or whether implementing that change is web compatible.

@sidhu663
Copy link

sidhu663 commented Oct 3, 2017

+1, I find this a very strange limitation especially when the element in question is not actually hidden. It makes it much more fragile to use it given that a change to a parent container / component could break sticky behaviour entirely

@valtlai
Copy link
Contributor

valtlai commented Dec 10, 2017

It would be nice to make this work:

<div class="table-wrapper">
    <table>
        <tr><th>Foo</th><th>Bar</th></tr>
        <tr><td>sin</td><td>cos</td></tr>
        <tr><td>tan</td><td>baz</td></tr>
        ...
    </table>
</div>
.table-wrapper {
    overflow-x: auto;
}

th {
    position: sticky;
    top: 0;
}

See demo

I need both horizontal scrolling and sticky header.

@SimplyPhy
Copy link

SimplyPhy commented Feb 11, 2018

I'm building a table template in SCSS which requires this functionality in many situations.

Thankfully, everything now works in Chrome [63], Edge [41], Firefox [59] and Safari [11].
Safari requires -webkit-sticky, and Firefox 59 is the current dev version (unreleased).

Here's a basic test case for overflow-x and overflow-y support:
https://codepen.io/SimplyPhy/pen/oEZKZo

@jonjohnjohnson
Copy link

jonjohnjohnson commented Feb 11, 2018

@SimplyPhy was going to comment and say safari just needed prefix for sticky, but now I see you updated your comment. Though safari still has an issue 'sticking' your td:first-child {top: 0}, I'm guessing because it doesn't calculate the top edge of it's parent tr like other browsers. But if you remove that property declaration, the behavior is probably how you want it?

@SimplyPhy
Copy link

Yup, thanks @jonjohnjohnson and nice find.

@natematykiewicz
Copy link

I have the following rule:

html, body {
  overflow-x: hidden;
}

and because of it, I'm not able to use position: sticky; at all. I'm trying to add some submit buttons on a form, and have them sticky to the bottom of the window position: sticky; bottom: 0; when outside the viewport. It works great and I love the feature, but this overflow-x rule prevents it from working. It sounds very similar to what @niutech mentioned.

@jonjohnjohnson
Copy link

@natematykiewicz though not ideal, I believe all current browser versions support sticky behavior if you alter your DOM structure so that the scrolling element is inside the body/html, filling out the screen. This does make it so browsers that resize the viewport on scrolling of the document.scrollingElement won't resize, such as ios safari. Notice the example code @SimplyPhy provided with for his table utility. Hope that helps until the spec is figured out.

@natematykiewicz
Copy link

@jonjohnjohnson my previous comment was that I have overflow-x: hidden; on the body and html tag, and that prevents position: sticky; from working on anything. I did just realize that if I put the overflow-x: hidden; on only the body or the html (but not BOTH), then it works (this is in Chrome on a Mac). So, maybe I can actually adjust my CSS to get the best of both worlds. It's just odd that preventing the body and html from horizontally scrolling means that I can no longer get elements to stick to the top of the screen. I could see overflow-y: hidden; causing problems, but not overflow-x: hidden;.

@jonjohnjohnson
Copy link

@natematykiewicz

The computed values of both overflow-x & overflow-y are...

as specified, except with visible computing to auto if one of overflow-x or overflow-y is not visible - css-overflow-3/#overflow-properties

This is what causes our issue and is spec compliant.

Furthermore, check your "(but not BOTH)" solution in safari, including iOS, because that approach hasn't historically given people what they have sought out. -> overflow-xhidden-doesnt-prevent-content-from-overflowing.... Again, the most robust solution is to scroll an element WITHIN the body, not the body/html/document. Good luck.

@jonjohnjohnson
Copy link

Just wanted to make a note for others about how allowing general overflow: hidden ancestors to not be the "scrolling context" for a sticky element isn't always desirable. When using a modal view that programmatically turns off/on scrolling with hidden/auto a sticky element would jump around the screen between the swapping scrolling contexts.

Also, when we (eventually) have overflow: clip implemented everywhere, this should no longer be an issue?

@sudheer-gowrigari
Copy link
Member

sudheer-gowrigari commented Jul 25, 2018

@SimplyPhy in your codepen (https://codepen.io/SimplyPhy/pen/oEZKZo ) on the table,Is there any way to restrict the scroll on header content. Scroll looks odd on the header.

@moritzjacobs
Copy link

For anybody stumbling upon this issue via google, here's a workaround (at least for my case) using a polyfill: https://stackoverflow.com/a/52420677/4903358

@protoEvangelion
Copy link

For my use case, one of the ancestors in the DOM had overflow-x: hidden;. My goal was to simply hide horizontal scrollbar, so this may not apply to your use case.

The way I fixed it was removing overflow-x: hidden; from that element in the DOM and adding it to body instead!

body {
  overflow-x: hidden;
}

@damienroche
Copy link

damienroche commented Jan 30, 2019

@protoEvangelion I realized after trying that the solution does not work on mobile browsers :(
https://stackoverflow.com/questions/14270084/overflow-xhidden-doesnt-prevent-content-from-overflowing-in-mobile-browsers

@jonjohnjohnson
Copy link

jonjohnjohnson commented Jan 31, 2019

@damienroche I believe androids webview behaves according to spec here, but it's iOS that doesn't. If you want to chip in and voice concern about their noncompliance here's the public ticket.

WebKit Bugzilla - Bug 153852 - with overflow:hidden CSS is scrollable on iOS

@AMUCOMO
Copy link

AMUCOMO commented Sep 7, 2022

Well as told here (https://www.gatlin.io/blog/post/stuck-on-position-sticky) the very sad cross axis problem is indeed very unfortunate. Especially considering this (https://stackoverflow.com/questions/6421966/css-overflow-x-visible-and-overflow-y-hidden-causing-scrollbar-issue).

@emilio
Copy link
Collaborator

emilio commented Sep 7, 2022

Well as told here (https://www.gatlin.io/blog/post/stuck-on-position-sticky) the very sad cross axis problem is indeed very unfortunate. Especially considering this (https://stackoverflow.com/questions/6421966/css-overflow-x-visible-and-overflow-y-hidden-causing-scrollbar-issue).

That seems like it'd be fixed by using overflow-y: clip instead, which doesn't cause the creation of a scroll containers.

@AMUCOMO
Copy link

AMUCOMO commented Sep 7, 2022

Well as told here (https://www.gatlin.io/blog/post/stuck-on-position-sticky) the very sad cross axis problem is indeed very unfortunate. Especially considering this (https://stackoverflow.com/questions/6421966/css-overflow-x-visible-and-overflow-y-hidden-causing-scrollbar-issue).

That seems like it'd be fixed by using overflow-y: clip instead, which doesn't cause the creation of a scroll containers.

Sadly not. At least for Firefox and Edge overflow: auto clip; gets converted to overflow: auto hidden;

So we are at square one again.

I've found some comparisons for those conversions, but this sites neglect to include clip in their tests:
https://www.brunildo.org/test/Overflowxy2.html
https://www.edv-lehrgang.de/verhalten-bei-overflow-x-und-overflow-y/

@emilio
Copy link
Collaborator

emilio commented Sep 7, 2022

Well, that is working as defined in https://drafts.csswg.org/css-overflow/#overflow-properties:

The visible/clip values of overflow compute to auto/hidden (respectively) if one of overflow-x or overflow-y is neither visible nor clip.

but the link you pasted was about x: visible and y: hidden unnecessarily creating scrollbars, which can be prevented by using x: visible and y: clip.

@AaronBeaudoin
Copy link

AaronBeaudoin commented Oct 4, 2022

This feature is useful for more than just horizontal scrolling on tables.

In my case the problem is combining rounded corners with sticky group headers inside a panel. Right now, the easiest way to give a panel/element rounded corners is to do border-radius: <whatever>px combined with overflow: hidden to prevent child elements with backgrounds from bleeding over the rounded areas at the corners. But that then prevents me from using position: sticky on any elements inside the panel.

My solution has been to not use border-radius or overflow-hidden and instead use a clip-path: inset(0 0 0 0 round <whatever>px) on the panel, but that comes with a new issue in my case (a bug with Chrome that seems to only occur when I'm using clip-path on huge areas such as panels that comprise most of the page) so clip-path is not an option either. Right now it's looking like I'm going to have to just use JS and avoid CSS position: sticky completely, which is really sad.

By the way, the Chrome issue is here.

@DavidJCobb
Copy link

DavidJCobb commented Dec 26, 2022

clip-path is unreliable for that use case for other reasons as well. I recently got subpixel rounding errors that caused a browser (don't recall whether it was Firefox or Chrome) to clip a 1px border off of an element, even though that border should have fallen within the specified path.

Anyway, it's incredibly unfortunate that setting overflow-x: auto on an ancestor causes position: sticky; top: 0 to become locked to that ancestor's always-zero scroll Y-position.

@fantasai
Copy link
Collaborator

fantasai commented Jan 6, 2023

OK, @tabatkins and I filed a new issue about having an element be sticky to multiple scrollports: #8286

As far as we can tell, the rest of this issue should be handled by appropriate use of either display: flow-root or overflow: clip. @hunboy, @jonjohnjohnson, can you confirm?

@jonjohnjohnson
Copy link

@fantasai confirmed! The discussion in this issue has become so broad that it's hard for newcomers to easily understand which aspects are and are not outstanding.

@evgeniy-kolmak
Copy link

The only thing that helped for my detection overflow: clip. I was able to make friends with postion: sticky

@akatuku
Copy link

akatuku commented Aug 11, 2023

I concur overflow: clip is helpful. For my use case, I created a custom scroll bar that mimics native scroll behavior it is doable(used only vanilla JS). I did notice some perf issues, but throttling and debouncing did help with perf.

@towerofnix
Copy link

towerofnix commented Jan 10, 2024

Hey, just commenting since we have a similar situation as @AaronBeaudoin brought up in #865 (comment) but with some extra nuance that makes overflow: hidden appealing, and not a detail I've seen brought up here yet.

Big edit: It turns out I'm just wrong about overflow: hidden. So this comment is left for posterity since it indicates what i was trying to do, and I sure thought overflow: hidden was the solution (apart from its sticky issue), but no. It has the same absolute-descendant-cropping behavior as overflow: clip and contain: paint, provided the absolute descendant is relative to a position: relative ancestor-between element.

One of the greater benefits of overflow: hidden is that it clips "normally" positioned children, but not children with position: absolute. In other words you can use it to cut off corners of static children - or, in principle, sticky children! - that "seep out" past the rounded corners of the parent. But you're left with flexibility: if you do need other children to leave that containing box, while still being placed relatively, position: absolute is your go-to. (In a position: relative parent of course.)

We're taking advantage of basically this overflow: hidden behavior for hover-tooltips beneath links (in addition to cleaning the rounded corners of the containing panel):

Floaty-looking box positioned neatly beneath a hovered link in ordinary text content, on top of other text content.

But in practice we aren't able to use overflow: hidden, of course, because it prevents the sticky heading element from scrolling with the page. So instead we're using contain: paint at the moment (overflow: clip is fine too, but worse browser support, and the same issue ahead). And this works fine... until we get to that case where overflow: hidden's true power shines... and contain: paint sure doesn't!

A rather tall tooltip near the bottom of a page. Where the main panel cuts off and the footer begins, the tooltip gets chopped in half!

It seems like the only workable solution here is to set overflow-y: scroll on the main element, but this seriously alters and conflicts with the layout of the site (you're supposed to scroll the page, not the main content panel).

We could position the tooltips with JavaScript and separate them outright from the main page layout tree, but it's such a janky solution. Placement in the HTML tree, statically generated, and positioned relatively to a parent position: relative link isn't something we're interested in leaving behind, but it's not clear what current solutions don't involve using contain: paint or similar - and that's not compatible with position: absolute elements that sometimes extend past the containing element's border.

@JacobDB
Copy link

JacobDB commented Jan 10, 2024

@towerofnix unfortunate that that's not the case, I actually ran in to similar situation recently, though not related to position: sticky;; I have a slideshow that needs overflow: hidden; to crop off its sibling slides, but navigation buttons that need to escape the container. There's workarounds of course, with additional containers, but it'd be awesome to have a way to "escape" the overflow property somehow when needed.

@webark
Copy link

webark commented Mar 26, 2024

I would say, that for 95% of the usecases that run into this, they would be fine falling back onto using the window for placement. Perhaps just a "sticky-window" or something like that additional, could solve the issue, but not open the need to select the anhoring ansestor. You would just get your parent, or the window. And if you choose the window, things like overflow and the ancestors scroll position is not longer taken into account, cause it can just be relative to the window.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-position-3 Current Work
Projects
None yet
Development

No branches or pull requests