- From: Anton Prowse <prowse@moonhenge.net>
- Date: Sat, 07 Feb 2009 19:52:34 +0100
- To: "www-style@w3.org List" <www-style@w3.org>
Andrew Fedoniouk wrote: > > As we know[1] rendering of static children of elements happens in > following order: > > for all static children: > step #1: draw backgrounds of these elements; > step #2: draw content of these elements (text in particular); > > This algorithm (strange, imo) works in most cases but plays > badly with negative margins. I do not find this algorithm strange. It provides a way of ensuring, by default, that text remains visible under various situations in which overflow and negative margins are thrown into the mix. If, for each box in turn, the text were painted at the same time as the backgrounds, it would not be possible to achieve this visibility. > Here is an example of very strange (non-intuitive if you wish) effect > that can > be observed when negative margins are used: > > <html> > <head> > <style> > div.red { width:100px; height:100px; > background-color:red; margin-bottom:-100px; } > div.green { width:90px; height:90px; > background-color:green; } > div.red:hover { background-color:orange; } > div.green:hover { background-color:lime; } > </style> </head> > <body> > <div class="red">This text has red background.</div> > <div class="green"></div> > </body> > </html> > > Problem here is that due to [1] these two elements have non-trivial > overlaying structure. > Each point inside div.green participate in following layers: > > 1) background of div.red > 2) background of div.green > 3) content(text) of div.red > 4) content(text) of div.green > > This clearly produces logical errors in detection of div.red:hover and > div.green:hover > conditions. Try to move mouse over and near the text. This is not a logical error. The text boxes are the "uppermost" of the boxes under the mouse and so receive the hover event. These text boxes form part of the red element, which is thus the element to which the event is propagated. (Events apply to (pseudo-)elements, not boxes, of course.) When you hover over the green box away from the text, the green box is the "uppermost" of the boxes under the mouse and so its corresponding element reacts to the hover event. > That appears as a bug of the specification. Non-intuitive, non-logical, > etc. In no sense is this a bug in the specification. The behaviour is both intentional and desirable. In particular, if the specification were different on this point only, it would be impossible to reproduce the current behaviour (maximum text visibility) by other means, whereas it currently /is/ possible to obtain the results you seem to desire through the use of stacking contexts and z-index. > Possible resolutions of the problem: > > A) To change [1] so drawing of the element background and its content is > atomic: > > For each static child draw its background and content(text) on top of > it. > (That is how it is made in all UI systems I know) This does not achieve the effect you desire, because the now-atomic background+text boxes of non-positioned in-flow elements will still be rendered in document tree order, and merely setting negative margins will not cause an earlier box to be painted on top of a later box. Moreover, the rendering which this approach does achieve is undesirable: A stacking context consists of several painting layers onto which different types of box are painted. Currently, in between the stacking layers for background and text of in-flow non-positioned dependants ("descendants") comes the stacking layer for non-positioned floated dependants. So proposal (A) effectively moves the painting layer for floats to after the now-unified painting layer for background and text of in-flow non-positioned dependants. Overflow from floats would now obscure the any adjacent text boxes belonging to their containing stacking context. Again, if you /want/ this behaviour you can currently have it through the use of stacking contexts; but if this behaviour were not the default you would not be able to reproduce it. > B) Treat all elements having negative margins as a separate layer laying > over > normal static children of the element thus rendering will happen this > way: > > step #1, for all static children *without* negative margins do: > step #1.1: draw backgrounds of elements; > step #1.2: draw content of elements (text in particular); > step #2, for all static children *with* negative margins do: > step #2.1: draw backgrounds of elements; > step #2.2: draw content of elements (text in particular); Whereas proposal (A) reorders the painting layers within a stacking context, proposal (B) introduces new painting layers in order to divide up the current behaviour in which boxes in the same painting layer are painted in document tree order. It is proposal (B) which was being discussed and formulated in your previous correspondence in a different thread. However, it does not seem well thought through. Your suggested change boils down to dividing the current painting step for text into two parts (text for non-negative- and text for negative-margined boxes) and inserting a new painting layer for the backgrounds of in-flow non-positioned dependants with negative margins in between. To avoid that this new painting layer for backgrounds sit on top of the painting layer for non-positioned floats (which would of course be unacceptable), the floats layer has to be moved to after the new text layer and we are more-or-less back to situation (A) and additionally we now have a different approach to honouring the document tree order. Moreover, with this approach, if divs D1, D2 and D3 are in-flow and non-positioned in a common stacking context, and are such that both D1 and D2 have a negative margin-bottom, then D2 would overlap D1 and D3 would overlap D2. But this doesn't seem to solve the issue you were wishing to address, which was that by specifying a margin-bottom on D1 you wanted the bottom part of D1 to be rendered on /top/ of the top part of D2. To to address your issue you would actually have to differentiate between boxes with negative margin-top, boxes with negative margin-bottom and boxes with negative top and bottom margin in some way, and treat each case not only differently but also with respect to the margin values of the surrounding element; hence there are even more cases than these three, corresponding to the possible different combinations. The task of fleshing out the resulting algorithm - and expressing it in the specification - is not one which I would relish, nor one which has merit IMO. Tab Atkins Jr. wrote: > On Thu, Feb 5, 2009 at 2:57 PM, Andrew Fedoniouk > <news@terrainformatica.com> wrote: > >> Tab Atkins Jr. wrote: >> >>> This makes complete sense. Elements later in the document order are >>> later in the paint order as well. >>> >> Try this: >> >> <html> >> <head> >> <style> >> .layer div { width:100px; height:100px; } >> .layer div.principal { margin:-10px 0; width:110px; } >> .layer div:hover { background:gold !important; } >> </style> </head> >> <body> >> >> <div class="layer"> >> <div class="static" style="background:red">Red</div> >> <div class="principal" style="opacity:0.5; background:blue">Blue</div> >> <div class="static" style="background:green">Green</div> >> </div> >> <hr/> >> <div class="layer"> >> <div class="static" style="background:red">Red</div> >> <div class="principal" style="background:blue">Blue</div> >> <div class="static" style="background:green">Green</div> >> </div> >> >> </body> >> </html> >> >> Which rendering on these two div.layer's is more intuitive? >> > > Personally? The second. > > But as you said before, people can disagree about what's the most intuitive. > > My point is that the 'normal' rendering (document order maps to paint > order) isn't *un*intuitive, it provides an effect that can't be easily > replicated in any other way, and the alternatives *can* be easily > replicated through other methods. I am in complete agreement with Tab's three statements. Regarding the third, I suspect that the current algorithm was conceived to reflect the order in which rendering engines actually need to handle the various boxes: floats sit on top of the backgrounds of incident elements but push their line boxes to the side. So the rendering would appear to run in parallel to the calculations being made, in the order backgrounds-floats-inlines. (Not that this in itself is an argument in favour of the status quo, because presumably the rendering order doesn't have to match the calculation order. Rather, Tab's argument is the argument in favour.) However, we should note that the use of stacking contexts and z-index to obtain the result you require (viz, D1, D2, D3 with D2 on top of D1 and D3) does have consequences: by positioning D2 to "promote" it to a later painting layer, you are of course making it a stacking context which has ramifications for its descendants, and furthermore you are making it the containing block for its closest positioned descendants. This is not necessarily a desirable side-effect of what you wished to achieve. Andrew Fedoniouk wrote: > <html> > <head> > <style> > .layer div { width:100px; height:100px; } > .layer div.principal { z-index:-1; position:relative; margin:-10px 0; > width:110px; } > .layer div:hover { background:gold !important; } > .aside { position:fixed; left:70px; top:0; bottom:0; width:10px; > background:magenta; z-index:-1;} > </style> </head> > <body> > > <div class="layer"> > <div class="static" style="background:red">Red</div> > <div class="principal" style="background:blue">Blue</div> > <div class="static" style="background:green">Green</div> > </div> > <div class="aside" >.</div> > </body> > </html> > > In this example I just wanted to put ".layer div.principal" element > behind its siblings. Intuitive solution for me is to use negative margins. Why? Negative margins are not for putting things behind other things in some specific order. That's what stacking contexts and z-index are for. > But if I will start playing with z-indexes and positioning then the > problem expands to the interaction with the whole DOM tree. Of course. Box placement in the third dimension is controlled by the stacking mechanism, which is a global concept; it expected that one should have to deal with the tree as a whole in order to achieve a given result. > And about previous example: do you have an idea why opacity changes > the element position in rendering stack? CSS3-COLOR[1] states: "If an element with opacity less than 1 is not positioned, implementations must paint the layer it creates, within its parent stacking context, at the same stacking order that would be used if it were a positioned element with ‘z-index: 0’ and ‘opacity: 1’. If an element with opacity less than 1 is positioned, the ‘z-index’ property applies as described in [CSS21], except that ‘auto’ is treated as ‘0’ since a new stacking context is always created." (Alas this paragraph introduces yet /another/ undefined term, "stacking order" to an already ambiguous description of the stacking model in CSS21. The phrase "at the same stacking order" is equivalent to the phrase "on the same painting layer" which I have been using.) > What should intuition tell us there?:) This behaviour is related not to intuition but rather to implementation. CSS3-COLOR[1] states: "Since an element with opacity less than 1 is composited from a single offscreen image, content outside of it cannot be layered in z-order between pieces of content inside of it. For the same reason, implementations must create a new stacking context for any element with opacity less than 1." Cheers, Anton Prowse http://dev.moonhenge.net [1] http://www.w3.org/TR/css3-color/#transparency
Received on Saturday, 7 February 2009 18:53:20 UTC