Skip to content

[css-position][css-sticky] Position: sticky doesn't work when it has several HTMLElements with margin before it #3923

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

Closed
Konrud opened this issue May 11, 2019 · 7 comments

Comments

@Konrud
Copy link

Konrud commented May 11, 2019

Steps to reproduce the problem:

  1. Add <p> element with text and optionally with margin and some other elements before the element you want to set as a sticky
  2. Set position: sticky; top: 0; on the element, you want to be sticky
  3. Scroll down to see that element is actually stays in its place and doesn't behave as a sticky.

What is the expected behavior?
The element should behave as a sticky element.

What went wrong?
The element stays in its place and doesn't behave as a sticky.

Code example: http://bit.ly/2HeL6Si

NOTE:
Position sticky won't work until there are HTMLElements before it in the DOM e.g. <p> element with margin. If you, however, delete all the elements before it in the DOM position sticky will work as expected.

Chrome version: 74
OS Version: Windows 7

@jonjohnjohnson
Copy link

jonjohnjohnson commented May 11, 2019

@Konrud Your example has less to do with margins and more to do with the fact that the ancestor within which your sticky element is attempting to stick[1] has a height set too short to be in view when scrolling down to view/reach the sticky element. In other words, remove the height:720px; declaration on your body element and everything should behave as expected.

[1] Creation of Sticky-Constraint Rectangles - https://drafts.csswg.org/css-position-3/#sticky-pos

Intersection between the stickily positioned element and the bottom of the sticky-constraint rectangle limits movement in any direction, so the offset never pushes the stickily positioned element outside of its containing block. However, when the element is free to move within its containing block as the page is scrolled, it appears to be pinned to the relevant flow root edges, similarly to a fixed position element.

@Konrud
Copy link
Author

Konrud commented May 11, 2019

@jonjohnjohnson thank you for your response.
But don't you think that is a bug or I should say an omission in spec?
I mean I think I should be able to declare height on the parent element and sticky element should still work as expected, especially since height: 720px; is not a small value at all and should be enough for the sticky element.
Regarding the reference to the spec that you provided. I've read this one and I know that if you define overflow: auto; on both <body> and <html> elements, sticky element will work as expected. This has to do with so-called scrolling-box (https://drafts.csswg.org/cssom-view-1/#scrolling-box).
But I haven't found anything to do with the height restriction.

@jonjohnjohnson
Copy link

jonjohnjohnson commented May 11, 2019

But don't you think that is a bug or I should say an omission in spec?

No, the sticky position scheme is not simply a "fixed-like" position scheme for any scrollport. It is something that allows an element to effectively stay in flow, retaining its geometry unless the "intersection" of its containing block and its scroll container allow it to stick to an edge/offset at a certain length. Key here being that it needs room to move within its containing block. And your example doesn't allow any space for it to move within the body that is scrolled thousands of pixels out of view by the time any of your sticky elements move into the scrollport. So720px is quite definitely a small value here, when your first sticky element is multiple thousands of pixels passed where it would become "unstuck", never actually allowing it to be in a "stuck" state.

This is a feature of sticky. Not a bug or omission.

Please read all the language about boxes and sticky-constraint rectangles in that section, because sticky is not just about vertical scrolling, it doesn't mention height specifically, instead it mentions boxes, which encompass height and width.

See this demo to understand why it's important for a sticky element to be constrained within a containing block, here the nearest block level ancestor. Without which, every sticky element could never become "unstuck" at any point, each staying in view and covering each other. Think about how alphabetical label are treated in contacts/phonebook apps like on iOS? See how sticky labels can "push" each other out from being "stuck" when reaching the end of their containers box/height?

Screen Recording 2019-05-15 at 06 28 PM

https://jsfiddle.net/qLw95pxc/

@fantasai fantasai added the css-position-3 Current Work label May 16, 2019
@Loirooriol
Copy link
Contributor

@Konrud If you want the height: 720px, then you can make the body be the scroll container, e.g. html, body {overflow: auto} (html is needed to avoid propagation)

@jonjohnjohnson
Copy link

@Konrud If you want the height: 720px, then you can make the body be the scroll container, e.g. html, body {overflow: auto} (html is needed to avoid propagation)

This is if you are ok with your scrollport not matching the size of the window, leaving extra unused space in the viewport or worse cutting off parts of this body scrollport when the viewport is shorter than 720px. You also lose browser viewport/root scroller "conveniences", such as operas and iOS Safaris ability to click on the browsers chrome to scroll to the top of the document or chromes overscrolling rubber banding.

@tabatkins
Copy link
Member

Ah, yeah, this is an odd intersection of some features.

The stickypos's parent (and containing block) is body, which is fixed to 720px; its content then overflows (and is caught by the html element being scrollable by default). As @jonjohnjohnson said, stickypos won't "stick" an element past the bounds of its containing block; since the CB is a 720px-tall rectangle at the top of the document, and the fixpos element is far below that, already outside the bounds, it won't move any further due to stickiness.

That's why you're seeing it stick if there's nothing preceding it - if the stickypos element starts out within the 720px-tall body's bounds, then it'll be allowed to stick a little bit (moving about 400px down, until it hits the bottom of the body element). But if you scroll further than that, it gets caught and scrolls up, trapped by the body element's bounds.

If you remove the height:720px from body, allowing it to grow to contain all its contents, it'll work as you expect, and the sticky bits will stick all the way down. Alternately, as @Loirooriol said, you can force the body to be a scroll container, by explicitly setting both html and body to overflow: scroll;; that has some unfortunate additional effects, however.

Since I don't understand why the height: 720px is there in the first place (it doesn't seem to have any visual effect on the page), I'm closing this issue as Working As Intended.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants
@jonjohnjohnson @tabatkins @fantasai @Loirooriol @Konrud and others