- From: Anton Prowse <prowse@moonhenge.net>
- Date: Thu, 26 Aug 2010 01:18:16 +0200
- To: www-style list <www-style@w3.org>
- CC: Bruno Fassino <fassino@gmail.com>, fantasai <fantasai.lists@inkedblade.net>
On 25/08/2010 17:47, Bruno Fassino wrote:
> I've not been able to follow all the latest details of this
> discussion, anyway I've two naive questions.
>
> On 08/23/2010 03:40 PM, fantasai wrote:
>>
>> # Computing the clearance of an element on which 'clear' is set is
>> # done by first determining the hypothetical position of the element's
>> # top border edge within its parent block.<del>This position is
>> # determined after the top margin of the element has been collapsed
>> # with previous adjacent margins (including the top margin of the
>> # parent block).</del> <ins>This position is the same as the where
>> # the actual top border edge would have been if the element had a
>> # non-zero top border and its 'clear' property had been 'none'.</ins>
>
>
> Is "top" in the above last line intended? If yes, I must have missed
> something, since I thought the discussion was moving toward using
> "bottom" there.
It was... but then the pieces of the puzzle finally fitted together, and
I was able to see what 9.5.2 was trying to say and what the WG was
hoping it says.
It turns out that what 9.5.2 is trying to say is:
"Use some notion of hypothetical top border position to determine if the
clearing element is past the relevant floats. If clearance is necessary
then the appropriate margin collapsing is inhibited, and the clearance
is set to the greater of the amount necessary to put the real top border
flush with the bottom of the float and the amount necessary to put it
where the hypothetical border position was (to cover the case where
inhibiting the margin collapsing causes the float to move upwards)."
What the WG was hoping it says is the same as the above but with the
proviso that there be no unexpected loss of margins and no unexpected
whitespace.
Trouble is, as test cases show, the notion of hypothetical top border
position used in the spec (namely, temporary top border) doesn't satisfy
the WG's goal... and nor does the notion we were contemplating in which
a temporary bottom border is introduced. This deters us from both these
notions.
So we need to go back to first principles to understand what we're
trying to achieve. A good first step is to ignore the possibility of a
self-collapsing clearing element. Then we all intuitively know what we
would like the hypothetical border top position to be: it's merely the
"obvious"/real border top position. It makes complete sense to use this
to determine if clearance is necessary, and the case for using some
other notion would have to be boldly fought, since any author would be
likely to say, "hang on, that browser's introduced clearance when it's
patently obvious it wasn't necessary" (or vice versa). We violate the
WG's goal, even for "easy" cases, if we use any other notion. (We were
getting ahead of ourselves in the earlier threads when trying to choose
between other, different notions; we should have already dismissed them
all on the basis of much easier test cases than the ones we were
considering.)
As you noted, the difficulty for my preferred notion – real top border
position – arises when the clearing element is self-collapsing, because
the spec's convention for this position is non-intuitive, which in turn
makes deciding upon the desired clearance behaviour rather difficult.
(Although we're already winning, because we've confined any ugly
renderings to atypical, self-collapsing cases.)
Let's see what happens in your test case:
> On 08/23/2010 7:23 PM, Anton Prowse wrote:
>>
>> I suggest the
>> following change to resolve both this issue and Issue 158:
>>
>> # Computing the clearance of an element on which 'clear' is set is
>> # done by first determining the hypothetical position of the element's
>> # top border edge within its parent block<del>. This position is
>> # determined after the top margin of the element has been collapsed
>> # with previous adjacent margins (including the top margin of the
>> # parent block).</del> <ins>as if the value of its 'clear' property
>> # had been 'none', as per the rules in 8.3.1.</ins>
>> #
>> # If this hypothetical position of the element's top border edge is
>> # not past the relevant floats, then clearance<del>must be</del>
>> #<ins>is introduced and margins collapsed anew according to the rules
>> # in 8.3.1.
>> #
>> # Then the amount of clearance is</ins> set to the greater of:
>> #
>> # 1. The amount necessary to place the<ins>top</ins> border edge of
>> # the block even with the bottom outer edge of the lowest float
>> # that is to be cleared.
>> # 2.<del>The amount necessary to make the sum of the
>> # following...</del>
>> #<ins>The amount necessary to place the top border edge of the
>> # block at the previously-computed hypothetical position.</ins>
>
>
> I remember we already discussed the idea to use full (normal) margin
> collapsing to compute the hypothetical position, and we saw this
> produces "unexpected" results in some cases, like this one:
>
> <div style="background: yellow">before</div>
> <div style="background: lime">
> <div style="float: left; height: 50px; width: 100px;
> background:blue"></div>
> <div style="clear: left; margin-bottom: 100px"></div>
> </div>
> <div style="background: yellow">next</div>
>
> Unless I'm missing something, your formulation still produces an
> "unexpected" result here.
Indeed, it's a nasty result. I analyzed this test case in detail in
[1], but allow me to quickly summarize that again:
First, assume clear:none. The float's parent P is self-collapsing, so
by 8.3.1.6.2, P's top border position is 100px below the bottom of
"before". The clearing element C is also self-collapsing, and so by
8.3.1.6.1, C's top border position is the same as P's. C's top border
position is not past the float and so we introduce clearance.
C no longer collapses its top margin with P's top margin. We recompute
the layout, and we're in that funny case where the float moves upwards.
Specifically, the float is now flush with the bottom of "before". For
C's current position, 8.3.1.6.2 applies and we find that it is also
flush with the bottom of "before". _We observe that inhibiting the
margin collapsing moved both P's and C's top border position upwards._
The clearance is then the greater of 50px (the amount needed to move C's
top border downwards to below the float) and 100px (the amount needed to
move it to the previously-calculated hypothetical position). Hence
clearance is 100px, C's bottom margin extends a further 100px downwards,
and "next" is 200px below the bottom of "before".
Ugly; we've pushed "next" down too far. In my original analysis, I
claimed this to be a consequence of the WG's 2007 decision to use bottom
border to determine the top border of the lime div P (through
8.3.1.6.2). Had top border been used instead, as the spec used to
require, then P's hypothetical top border position would have been
directly below "before", as would the top of the float and the
hypothetical position of C's top border. Clearance would still be
introduced – but it wouldn't affect the top border position of the lime
div, and so the float would stay where it was, the clearance would be
50px and C would "move down" to just below the float, with "next" 100px
below that, as we might have liked.
However, now that I have more insight into the issue, I see that it the
situation is more subtle.
Firstly, equally nasty layouts occur with the pre-2007 convention for
top border position of self-collapsing elements. Sure, it fixes the
test case above, but what about the following one:
<div style="background: yellow">before</div>
<div style="background: lime">
<div style="float: left; height: 50px; width: 100px;
background:blue"></div>
<div style="clear: left; margin-top: 100px"></div>
</div>
<div style="background: yellow">next</div>
This time, the clearing element has a /top/ margin of 100px. Let's
assume the pre-2007 definition of top border position of self-collapsing
elements.
With clear:none, the float's parent P is self-collapsing, so by
8.3.1.6.2, P's top border position is flush with the bottom of "before".
The clearing element C is also self-collapsing, and so by 8.3.1.6.1,
C's top border position is the same as P's. C's top border position is
not past the float and so we introduce clearance.
C no longer collapses its top margin with P's top margin. We recompute
the layout, and P's top border position (and hence the float) stays
flush with the bottom of "before". For C's current position, 8.3.1.6.2
applies and we find that it is 100px below the bottom of "before".
Clearance is -50px, and C – and hence "next" – moves upwards... meaning
that C's top margin is effectively ignored.
So you lose either way; the existence of the problem is *not* dependent
on the choice of temporary border edge used in 8.3.1.6.2 to determine
top border position.
Secondly, it's not the use of 8.3.1.6.2 /per se/ that causes our
problem; rather, it's the use of that rule when the self-collapsing
element has clearance. Let's look again at the first example.
With clear:none, P's (and hence C's) position was "low", relative to the
100px collapsed margin to which C's margins contribute. Inhibition of
margin collapsing forces P and the float upwards, and also means that
C's top border position is calculated differently: suddenly it's "high"
(in fact, incident with P's), relative to the 100px collapsed margin.
Now, proceeding to use clearance to push C back downwards isn't what
we're complaining about, assuming that we accept the desirability of
Calculation 2 in the first place, since we have no basis on which to
distinguish self-collapsing elements from others in this regard. What's
problematic is that C's bottom margin is now in some sense "below" C's
position, pushing subsequent elements even further downwards when
clearance moves C downwards, whereas initially it was "above" it when
clear:none was assumed.
We can solve this with the following modification of 9.5.2, which
bypasses 8.3.1.6.2 once clearance is introduced, for the situation that
causes us difficulty:
# If this hypothetical position of the element's top border edge is
# not past the relevant floats, then clearance <del>must be</del>
# <ins>is introduced and margins collapsed anew according to the rules
# in 8.3.1. However, if the element's top and bottom margins
# collapse, and the resulting margin collapsed with the parent's top
# margin when the hypothetical position was calculated, then the
# element's top border position is taken to be the same as that of a
# hypothetical immediately-following sibling with zero top margin and
# non-zero top border rather than that specified in 8.3.1.
#
# Then the amount of clearance is</ins> set to the greater of:
This introduces a change only in the narrow case we're interested in.
What we're doing is engineering the situation to ensure that the
element's bottom margin doesn't influence the size of the collapsed
margin below the redefined top border edge position. This definition
change doesn't matter to the clearing element itself, since the whole
point of clearance is to shift the rendering so that the clearing
element ends up where we want it; the mechanics of how the end result is
achieved is unnoticeable. It remains to verify that it doesn't harm any
otherwise-good test cases by influencing the position of surrounding
elements, as follows.
It doesn't affect the clearing element's children, since these are
necessarily self-collapsing and so their position remains the same as
that of the element itself (by 8.3.1.6.1), whatever that is defined to
be. (Note that this is unlike the situation arising from some unnatural
notion of hypothetical top border edge being used in 9.5.2, in which a
child's hypothetical position could end up higher than its parent's
actual position, causing pathological cases when both parent and child
have clear set. That is another reason to discount any notion of
hypothetical top border edge other than the "real" one I've been
advocating throughout this thread.)
It doesn't affect the clearing element's previous siblings since they
share the parent's position – a relationship that is unaffected by the
introduction of clearance, and a post-clearance position that is
unaffected by how the position of the clearing element is defined (and
which is necessarily not below the bottom of the relevant floats, and
hence is above the post-clearance position of the clearing element).
It doesn't affect the clearing element's subsequent siblings, in the
sense that these remain in positions at least as low as the clearing
element's position (due to 8.3.1.6.2's bottom border device).
I believe that this addition to my original proposal excludes all
undesirable renderings, and hence fulfills the WG's original goal.
Moreover, it is the only proposal known to do so.
[1] http://lists.w3.org/Archives/Public/www-style/2010Jul/0035.html
Cheers,
Anton Prowse
http://dev.moonhenge.net
Received on Wednesday, 25 August 2010 23:20:37 UTC