Skip to content

[css-overflow] CSSOM scrollWidth/scrollHeight behaviour of "overflow: clip". #5572

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
bfgeek opened this issue Oct 1, 2020 · 21 comments
Closed

Comments

@bfgeek
Copy link

bfgeek commented Oct 1, 2020

Consider:

<!DOCTYPE html>
<div id="target" style="width: 50px; height: 50px; overflow: clip;">
  <div style="width: 100px; height: 100px; background: lime;"></div>
</div>
<script>
console.log(target.scrollWidth);
console.log(target.scrollHeight);
</script>

What should the console log?

FF currently logs 100, we Blink currently log 50 as we saw some additional performance opportunities (and it isn't "scrollable" like other overflow types), but happy if this should be 100.

@bfgeek
Copy link
Author

bfgeek commented Oct 1, 2020

cc/ @emilio @tabatkins

@bfgeek bfgeek added the css-overflow-3 Current Work label Oct 1, 2020
@x-Jake-x
Copy link

x-Jake-x commented Oct 1, 2020

The draft currently says

ISSUE 3: should overflow: clip also clip the scrollable overflow or should it remain a pure paint-time operation, which would mean that scrollable overflow, while invisible, would still be scrollable.

but it also says

clip This value indicates that the box’s content is clipped to its overflow clip edge and that no scrolling user interface should be provided by the UA to view the content outside the clipping region. In addition, unlike overflow: hidden which still allows programmatic scrolling, overflow: clip forbids scrolling entirely, through any mechanism, and therefore the box is not a scroll container.

My first impression is that scrollWidth and scrollHeight should be able to be retrieved programmatically in order to determine to what extent something can or should be scrolled. Since clip by definition seems to indicate it cannot be scrolled, the "50" that @bfgeek mentions above seems to make sense.

HOWEVER -- if we look at ISSUE 3 a bit more technically -- it seems like it would completely redefine the clip value entirely by saying "This IS a scrollable container, but scrolling to the end of the overflow area would not show more of the content that was previously clipped." I can't think of a practical use case for this, but that doesn't mean there isn't one.

The draft also says:

The scrollable overflow area is the union of:
the box’s own content and padding areas

As an impractical use case (at least impractical to me at the moment)...

When we say "the box's own content" -- are we referring to elements, text, etc. within the parent box -- and is background considered part of that "content"? If background is not defined as part of content, then allowing ISSUE 3 to define clip as a pure paint-time operation and be scrollable would have to programmatically allow the scroll of the area in such a way that additional parts of the background could be revealed while simultaneously shrinking and shifting the box content -- think a "parallax" effect around the content -- at which point the "100" seems to make more sense to me.

@emilio
Copy link
Collaborator

emilio commented Oct 1, 2020

cc @MatsPalmgren, I think returning 100 is just a bug. Per the blame on https://bugzilla.mozilla.org/show_bug.cgi?id=833542 the intention seemed to be that it behaves as if it was overflow: hidden.

@emilio
Copy link
Collaborator

emilio commented Oct 1, 2020

(And moderately sure we clip the scrollable overflow for layout)

@bfgeek
Copy link
Author

bfgeek commented Oct 1, 2020

overflow: hidden element are scrollable as which is why 100 for them makes sense, e.g. they respect scrollTop etc (overflow: clip doesn't respect scrollTop etc).

I'm fine with returning 100 here for clip as well, the additional optimisation if you have clip in both axis is that you can skip the scrollable-overflow calculation (I'm pretty sure), but this isn't a big deal.

@x-Jake-x
Copy link

x-Jake-x commented Oct 2, 2020

An example of what actually happens with clipped content:

https://jsfiddle.net/mq85u93s/

@x-Jake-x
Copy link

x-Jake-x commented Oct 2, 2020

An example using explicit JS instead of CSS animations, and moving the contents of the clipped area instead of the background of the containing box:

https://jsfiddle.net/pzxrscbq/

@x-Jake-x
Copy link

x-Jake-x commented Oct 2, 2020

Note that these behaviors do not use the "scrollLeft" property, which seems to be correctly restricted, so perhaps they do not actually count as scrolling -- but do UAs actually treat them as clipping?

@MatsPalmgren
Copy link

Blink's behavior seems more useful since any overflow is clipped and isn't seen as scrollable overflow by the parent, unlike an overflowing overflow:visible element. I filed https://bugzilla.mozilla.org/show_bug.cgi?id=1668714 to fix this in Gecko.

It's worth noting that clip may apply in just one of the axes in which case we should include any overflow in the other (visible) axis.

Gecko also have a number of cases where we treat hidden as clip, and contain:paint + overflow:visible as overflow:clip etc. I'll exclude those too for now.

@x-Jake-x
Copy link

x-Jake-x commented Oct 2, 2020

It sounds like Issue 3 is moot, in that case, as clipped content wouldn't be seen as scrollable.

This seems to me as though this makes the only difference between clip and hidden to be that clip is considered non-scrollable, while hidden is considered programmatically scrollable.

Was the intent at any point to make it so that content in a clipped overflow would be clipped by the containing element's box, permanently, so that it would never show? Or was the intent to simply deal with scrolling using "scrollLeft" and "scrollTop", continuing to leave the contents programmatically manipulable in a simulated scrolling fashion (as in the above examples) using means other than scrolling?

It sounds like the decision to limit the "scrollWidth" and "scrollHeight" to the area of the containing elements box makes sense, as the element would technically be "not scrollable" (despite the not visible contents still being available through programmatic manipulation). However, I feel like this removes an easy way to determine the width and height of the content of the containing element, which still exist despite "clip" being set. Child elements of a clipped container would need to be contained inside another non-clipped element to easily get their total "scrollWidth" and "scrollHeight".

...I wanted to create an example to demonstrate this to myself, but I can't because Chrome/85.0.4183.123 and Chrome/85.0.4183.121 report the full width of the content area of the containing element. They don't limit it just the width of the containing box. Where can I test what Blink is currently doing @bfgeek?

Here is my base test case for what you gave: https://jsfiddle.net/6hfgm4yx/

Update: Chrome/88.0.4281.0 also reports 100 for me, not 50.

@bfgeek
Copy link
Author

bfgeek commented Oct 2, 2020

Where can I test what Blink is currently doing

On Chrome M88 you'll need to pass --enable-blink-features=OverflowClip in the command line.

Ian

@emilio emilio added the Agenda+ label Oct 5, 2020
@bfgeek
Copy link
Author

bfgeek commented Oct 5, 2020

@emilio @MatsPalmgren What is your behaviour if contain: paint instead of overflow: clip ?

@x-Jake-x
Copy link

x-Jake-x commented Oct 6, 2020

Ah -- whether it's contain: paint or overflow: clip, the child elements maintain their "scrollWidth" and "scrollHeight". In that respect, you're not really losing the ability to calculate what the size is for the interior of the container.

https://jsfiddle.net/6dcj0fye/

It really does feel like it should be 50 and not 100 in that case.

Really, I'd think this should be overflow: noscroll instead of overflow: clip to be more semantically indicative of what is happening -- you cannot use specific scrolling functionality with it in JS, it is not considered a scrolling container, it is not related to clip-path (which still allows scrolling with overflow: scroll: https://jsfiddle.net/dgs71mo5/ and maintains the calculated scrollWidth and scrollHeight of its child elements) -- but I guess that ship has long since sailed.

@tabatkins
Copy link
Member

overflow:clip is meant to be like overflow:visible, except it hides stuff. It's analogous to how overflow:hidden is like overflow:scroll, but yeah, clip is very explicitly not scrollable.

So the answer here should be that clip returns the same value as visible does, whatever that is.

@x-Jake-x
Copy link

x-Jake-x commented Oct 6, 2020

In that case, clip and visible currently behave differently in Blink with the Chromium --enable-blink-features=OverflowClip argument:

https://jsfiddle.net/p8e769r5/

@emilio
Copy link
Collaborator

emilio commented Oct 6, 2020

Well sorting that out is the point of the issue.

Upon reflection I tend to agree with @tabatkins... I see how actually clamping the value could be useful, but also I tend to agree that it probably shouldn't behave differently than visible...

@x-Jake-x
Copy link

x-Jake-x commented Oct 7, 2020

Well, what exactly is the intention of the values reported in scrollWidth and scrollHeight?

@emilio
Copy link
Collaborator

emilio commented Oct 7, 2020

So they only really make sense on scroll containers, and they return the size of the scrollable area. But we need to return stuff there for compat even if you're not a scroll container. The idea for overflow: visible is that they return the sizes that it would've returned would it be a scroll container. So if we follow the same logic clip should probably be the same.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed CSSOM scrollWidth/scrollHeight behaviour of “overflow: clip”, and agreed to the following:

  • RESOLVED: scrollWidth/scrollHeight ignore overflow:clip when computing the value
The full IRC log of that discussion <Rossen_> Topic: CSSOM scrollWidth/scrollHeight behaviour of “overflow: clip”
<Rossen_> github: https://github.com//issues/5572
<fremy> Rossen_: iank_ raised this and emilo last added to the agenda
<fremy> Rossen_: who want to bring us up to speed?
<fremy> emilio: me
<fremy> emilio: we considered what scrollWidth/Height should return when overflow-clip is set
<fremy> emilio: there is not scrollbar
<fremy> emilio: but there is content out there
<fremy> emilio: the question is should we return the size as if there was scrollbar
<TabAtkins> +1 to behaving the same as visible, at least from first thoughts
<fremy> emilio: I think acting as visible is easier
<fremy> emilio: but iank_ said there is an optimization
<fremy> iank_: if you have overflow:hidden can be scrolled
<fremy> iank_: for visible, it still makes sense to return the full value, because it will cause scrollbars on ancestors
<TabAtkins> Hm, okay, Ian's making sense
<fremy> iank_: when you clip though, it's not useful, because it really does not exist
<fremy> iank_: but if you don't do that, you can return early
<fremy> iank_: because you don't need to calculate the scrollable overflow for the element when the value is fetched
<fremy> iank_: but it's not super important and only works if clip is in both directions
<fremy> smfr: the other option was what?
<fremy> iank_: report as if you had no children
<fremy> iank_: if the size is definite and you clip overflow, you don't need to layout the children
<fremy> iank_: but it's a small case
<fremy> smfr: and offsetWidth/offsetHeight?
<fremy> iank_: yes, this is the same as what I had in mind
<fremy> smfr: oh, in that case, yes, I prefer that option slightly
<fremy> Rossen_: other opinion?
<fremy> iank_: if I had to choose I would probably try to keep things consistent, but I really don't mind
<fremy> iank_: we just need to do something
<fremy> Rossen_: ok, let's get a summary of the two options and resolve
<fremy> iank_: option 1 is to do what we do when overflow:visible is applied (you need to layout the children to report that width)
<fremy> iank_: option 2: (ignoring overflow-clip-margin) report the size of the element ignoring whether or not you have something that will be clipped
<fremy> iank_: but if you have a clip margin, you need to do the full computation to see where inside that margin you land
<fremy> Rossen_: (restates the two proposals)
<fremy> fantasai: scrollWidth scrollHeight are asked on an element which is not scrollable
<fremy> fantasai: does that mean they are defined on all elements
<fremy> emilio: yes
<fremy> fantasai: ok, then, if we have a clipping, what happens if you have a border?
<fremy> iank_: they return content-box sizes, so it's a bit complicated
<fremy> fantasai: padding box you mean?
<fremy> iank_: yes, padding box size
<fremy> iank_: if you have no children, this is clientWidth/clientHeight, which is the padding box
<fremy> iank_: if you have a child, it returns whichever is bigger (the padding box or that child)
<fremy> iank_: so if you set overflow-clip with no margin, you can return the padding box because the child will not overextend
<fremy> iank_: but this only works if you dont have a margin
<fremy> iank_: if you have a margin, there could be an overflow
<fremy> iank_: so you need to do the full computation
<fremy> fantasai: the advantage of the doing like visible is that you get the value that tells you "how big should it be not to overflow"
<fremy> fantasai: the advantage of taking the clip into account, is that you know how much you will ask the parent scroller
<fremy> iank_: with proposal 2 you can't get answer 1
<fremy> iank_: but with proposal 1 you can compute the difference yourself
<fremy> Rossen_: this seems more clear now
<fremy> Rossen_: for "what you see is what you get" option 2 is nicer
<fremy> Rossen_: I don't want to do a straw poll, let's try to resolve
<fremy> Rossen_: can we resolve on option 2 which takes into account the visual clipping?
<Rossen_> q
<fremy> emilio: I lean towards option 1
<fremy> emilio: because I don't see strong arguments
<fremy> emilio: and for testing purposes, we can copy all the tests
<fremy> emilio: and we have shipped like that
<fremy> emilio: but this is not a super strong argument
<fremy> emilio: but given option 2 removes your ability to get the value of option 1
<fremy> emilio: I would rather we did 1
<fremy> Rossen_: if you want to get the bounds for the clip, what do you do?
<fremy> iank_: in script, you get the style of the clipping, then you do Math.min
<fremy> iank_: between scrollWidth and offsetWidth + the clip margin
<fremy> Rossen_: authors might not think about it
<fremy> Rossen_: and very often it's more useful to consider what is visible
<fremy> emilio: I don't disagree it's useful
<fremy> emilio: but scrollWidth is not the right API for that
<fremy> emilio: it's weird and legacy, so I would rather not change it
<fremy> Rossen_: ok let's do a poll
<fantasai> Given the arguments in favor of emilio's position, and the fact that you can get answers to the second question fairly easily with the first option but not the other way around, I'd be in favor of emilio's position
<fremy> Rossen_: because it's pretty split
<fremy> smfr: if we have scrollWidth behave like overflow:visible
<fremy> smfr: then you can't get the data and know if you can flip to visible without overflow
<fremy> fantasai: yes, option 2 hides info from authors you can't get otherwise
<fremy> fantasai: it's not great
<fremy> Rossen_: ok, then le'ts resolve for option 1
<fremy> RESOLVED: scrollWidth/scrollHeight ignore overflow:clip when computing the value

@chrishtr
Copy link
Contributor

The resolution made was that 50 is the correct answer in this example, and Firefox & Chrome already have that behavior.

Does the spec need to be edited to clarify this also? My impression is no.

@tabatkins
Copy link
Member

No, the resolution was that it should match visible, and thus be 100 in the OP's example.

Looking over CSSOM View, I'm pretty sure that nothing needs to be done here - the scrolling area calculations don't reference the 'overflow' value at all, and from my reading should indeed give the same value regardless.

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

9 participants