Tutorial Categories:

HTML/CSS JavaScript/AJAX Server-Side Marketing General Comp-Sci

z-index and the Order in Which Elements Are Painted On Screen

By Justin Poirier

The elements of an HTML page are painted on screen one-by-one by the user agent. This paper will explain the order in which they are painted, which becomes important when more than one element occupies the same space. As will be discussed, an element's place in this order can be altered by changing its "z-index" property, which only exists for positioned elements and can take on any integer value as well as "auto".

This order is defined using a general-purpose ordering of a page's elements, some of which may be grouped into modules that are dealt with atomically, with the assumption that the painting order of the elements within a module has been determined by a separate application of the ordering itself. In other words, the algorithm by which a user agent paints the elements in this order would be recursive. As such the ordering applies when considering the painting order within certain sections of a page, as well as the overall painting order of the page itself, the highest-level module to which it applies being simply the root element.

This ordering was created by the W3C and is part of the Candidate Recommendation of CSS 2.1. It appears on the left in the table below in the exact text published by the W3C. The ordering is very complex so there are understandably parts in the W3C specification which some readers have not found clear. In the right column of the table below we have provided another description of the ordering meant to be more clear, with each section of it appearing next to the section(s) of the W3C specification that describes the same thing plus or minus certain details.

Before reading the ordering, please understand the W3C's definition of "Tree Order" and "Element" as they apply to this discussion. The module to which the ordering applies will always consist of descendants of a single element which will be referred to in our alternate description of the ordering as "the context".

  • 1. If the element is a root element:
    • 1. background color of element over the entire canvas.
    • 2. background image of element, over the entire canvas, anchored at the origin that would be used if it was painted for the root element.

If the context is block-level, the background(s) and border(s) of the context.

If the context is inline it will be painted later in the ordering. Note that even when contexts are positioned, including cases where the "position" property is set to "absolute" or "fixed", they are still painted at one of these two locations in the ordering because the context is still technically "block" or "inline" (or a comparable value) as per the "display" property. This is also true of contexts where display is set to another value such as "run-in", as user agents will convert these elements to "block", "inline" or comparable after altering them appropriately.

When the context is a float, a block-level box is created so the background(s)/border(s) are painted now.

  • 2. If the element is a block, list-item, or other block equivalent:
    • 1. background color of element unless it is the root element.
    • 2. background image of element unless it is the root element.
    • 3. border of element.

    Otherwise, if the element is a block level table:

    • 1. table backgrounds (color then image) unless it is the root element.
    • 2. column group backgrounds (color then image).
    • 3. column backgrounds (color then image).
    • 4. row group backgrounds (color then image).
    • 5. row backgrounds (color then image).
    • 6. cell backgrounds (color then image).
    • 7. all table borders (in tree order for separated borders).
  • 3. Stacking contexts formed by positioned descendants with negative z-indices (excluding 0) in z-index order (most negative first) then tree order.

If the context's z-index is an int, all descendants whose z-index is a negative non-zero int, excluding any one of those that is a descendant of another one of the context's descendants that has an int z-index. These should be painted in z-index order, then tree order, with each painted atomically using a separate application of the ordering.

Note that the reason we've said to at times exclude an element is because referring to the atomic painting of the element of which it is a descendant presupposes its painting, and as such we've witnessed for the first time the "recursive" nature of the ordering. The W3C's explanation of this stage implies the recursion but also states that all descendants with appropriate z-index should be dealt with even though some will be painted as part of the atomic painting of others. This may lead to confusion.

Also note that we've said to paint "all descendants" that fit the stated criteria, and not just children. This is because, as we will see, certain types of element will be painted right in the present application of the ordering, without expecting them to deal with their own contents by a recursive application. One of the elements we are interested in could belong to one of these elements, and thus not be a child of the context. However, these elements are not the only ones that might come between the context and the element we are interested in. Later in the ordering we will make a recursive application for descendants with no z-index or a z-index of "auto". Because we said that the current step only applies to contexts with an int z-index, and because we will make a similar condition when we later deal with descendants with 0 or positive z-indices, these stages will not apply in the recursive application, leaving any descendants of that application's context with negative z-indices to be dealt with now. The W3C specification doesn't state the aforementioned conditions that cause this behaviour, instead only stating, prior to making a recursive application for a descendant without an int z-index, that the context was to be treated "as if it created a new stacking context, but..." (see later stages for full line). This may cause confusion.

It becomes clear at this point that the network of relationships describing which elements are painted by the application of the ordering to which other elements follows two rationales: one for those contexts with an int z-index and one for those without. For contexts without an int z-index, the ordering paints (possibly through recursion) all descendants except those with an int z-index which it ignores. For contexts with an int z-index, it paints all descendants, which might involve explicitly painting an element that was ignored by a recursive application made for a descendant without an int z-index.

Figure 1 shows this behaviour for an example tree of elements. Each red box contains a context and all descendants that its application of the ordering must paint (possible through recursion).

A graphical illustration of contexts as we have defined them, showing which paint which (as defined by the ordering, and as influenced by the z-index of each).
Figure 1

The W3C specification does not distinguish between contexts with and without an int z-index, instead referring to both types by the term "stacking context". In a detailed proposal to revise the specification, circulated on the W3C CSS mailing list in 2008, Anton Prowse has suggested introducing the term "painting context" to refer to contexts created for elements without an int z-index, and using the term "stacking context" only for elements with an int z-index.

  • 4. For all its in-flow, non-positioned, block-level descendants in tree order: If the element is a block, list-item, or other block equivalent:
    • 1. background color of element.
    • 2. background image of element.
    • 3. border of element.

    Otherwise, the element is a table:

    • 1. table backgrounds (color then image).
    • 2. column group backgrounds (color then image).
    • 3. column backgrounds (color then image).
    • 4. row group backgrounds (color then image).
    • 5. row backgrounds (color then image).
    • 6. cell backgrounds (color then image).
    • 7. all table borders (in tree order for separated borders).

The background(s)/border(s) of all non-positioned block-level descendants not including those already painted or going to be painted in a recursive application, in tree order.

We have already seen a case where such a recursive application is made, and will see more cases later in the ordering.

Note that the W3C specification, at this and other stages of the ordering, specifies that the relevant elements are "in-flow, non-positioned". The specification of "in-flow" seems meant to ensure the element has a display property value other than "none", since this is the only aspect of in-flow elements not part of the definition of "non-positioned" elements.

  • 5. All non-positioned floating descendants, in tree order. For each one of these, treat the element as if it created a new stacking context, but any descendants which actually create a new stacking context should be considered part of the parent stacking context, not this new one.

All non-positioned floating descendants not including those already painted in a recursive application, in tree order. Each of these should be painted atomically, using a recursive application.

  • 6. If the element is an inline element that generates a stacking context, then:
    • 1. For each line box that the element is in:
      • 1. Jump to 7.2.1 for the box(es) of the element in that line box (in tree order).

If the context is inline, the background and borders of the context, followed by its contents.

Note that at this point in the ordering, we've already passed through the stages at which certain types of elements are painted. While block level elements and floats will not exist inside an inline element (if placed there in code, the structure will have been altered by the user agent), descendants with a negative z-index might. If so, these elements will now be painted over by the background.

Also note the following with regards to how the W3C specification, at line 6.1.1, says to skip to line 7.2.1 for the box(es) of the context. Line 7.2.1 prescribes several steps to be taken on "each box that is a child of that element". However, when control has skipped to this line from line 6.1.1, as well as the case we will see later where control skips from 7.2.1.4.1.2 to 7.2.1, it seems obvious that it will actually be the box(es) created for the element itself, and not its children, that 7.2.1 will refer to. This may cause confusion.

  • 7. Otherwise: first for the element, then for all its in-flow, non-positioned, block-level descendants in tree order:
    • 1. If the element is a block-level replaced element, then: the replaced content, atomically.
    • 2. Otherwise, for each line box of that element:
      • 1. For each box that is a child of that element, in that line box, in tree order:
        • 1. background color of element.
        • 2. background image of element.
        • 3. border of element.
        • 4. For inline elements:
          • 1. For all the element's in-flow, non-positioned, inline-level children that are in this line box, and all runs of text inside the element that is on this line box, in tree order:
            • 1. If this is a run of text, then:
              • 1. any underlining affecting the text of the element, in tree order of the elements applying the underlining (such that the deepest element's underlining, if any, is painted topmost and the root element's underlining, if any, is drawn bottommost).
              • 2. any overlining affecting the text of the element, in tree order of the elements applying the overlining (such that the deepest element's overlining, if any, is painted topmost and the root element's overlining, if any, is drawn bottommost).
              • 3. the text.
              • 4. any line-through affecting the text of the element, in tree order of the elements applying the line-through (such that the deepest element's line-through, if any, is painted topmost and the root element's line-through, if any, is drawn bottommost).
            • 2. Otherwise, jump to 7.2.1 for that element.

          For inline-block and inline-table elements:

          • 1. For each one of these, treat the element as if it created a new stacking context, but any descendants which actually create a new stacking context should be considered part of the parent stacking context, not this new one.

          For inline-level replaced elements:

          • 1. the replaced content, atomically.

        Some of the boxes may have been generated by line splitting or the Unicode bidirectional algorithm.

      • 2. Optionally, the outline of the element (see 10 below).
    • 3. Optionally, if the element is block-level, the outline of the element (see 10 below).

The replaced content if the context is a block-level replaced element; and otherwise all non-positioned block-level descendants not already painted or going to be painted in a recursive application, in tree order, each of which will involve painting either its replaced content or all its non-positioned inline and inline-block descendants that aren't already painted in a recursive application, in tree order. Inline-blocks are painted using a recursive application.

For details on the painting of inline elements, see the W3C specification.

Recall that the background(s)/border(s) of block-level descendants were painted earlier in the ordering, so they don't need to be painted at this stage.

Note that inline elements are painted at the same time as their enclosing block-level elements; there is a commonly-held misconception that all block-level elements are painted (excluding their inline descendants), followed by all inline elements.

Note that outlines may be painted at this stage; see the W3C specification for details.

Note that, as mentioned in the last stage, line 7.2.1 prescribes several steps to be taken on "each box that is a child of" the element being painted, yet when control skips from line 7.2.1.4.1.2 to 7.2.1, it is in fact the box(es) created for the element itself, not its children, that the steps apply to. This may cause confusion.

  • 8. All positioned descendants with 'z-index: auto' or 'z-index: 0', in tree order. For those with 'z-index: auto', treat the element as if it created a new stacking context, but any descendants which actually create a new stacking context should be considered part of the parent stacking context, not this new one. For those with 'z-index: 0', treat the stacking context generated atomically.

All descendants with a z-index of "auto" not already painted or going to be painted in a recursive application, in tree order; and if the context's z-index is an int, all descendants with a z-index of 0 not already painted or going to be painted in a recursive application, in tree order.

Note that painting of descendants with a z-index of "auto" is not conditional, and therefore the rule regarding which ancestor paints elements with z-index "auto" and which descendants these elements can paint is like that for elements with no z-index at all.

  • 9. Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index order (smallest first) then tree order.

If the context's z-index is an int, all descendants with a positive non-zero z-index not already painted or going to be painted in a recursive application.

  • 10. Finally, implementations that do not draw outlines in steps above must draw outlines from this stacking context at this stage. (It is recommended to draw outlines in this step and not in the steps above.)

Finally, implementations that do not draw outlines in steps above must draw outlines from the context at this stage. (It is recommended to draw outlines in this step and not in the steps above.)

The original and only canonical version of the above passage from the W3C specification appears at http://www.w3.org/TR/CSS21/zindex.html. The W3C is not responsible for any content on this page not appearing at that URL. The alternative description of the ordering we have provided is non-normative.

The W3C specification points out two notes regarding this ordering.

Example Documents and Their Painting Orders

E.g. 1:

This example shows a case where the ordering defines the painting order with no recursive applications, and a block-level element gets painted later than inline elements.

Code:

<span id = "s1"></span>
<span id = "s2"></span>
<div id = "d1">
	<div id = "d2">
		<span id = "s3"></span>
	</div>
</div>
<img id = "i1" style = "display:block" src = "picture.jpg"></img>

Painting Order:

  1. Background(s)/border(s) of parent
  2. B/b of anonymous box created (as per the visual formatting model) for the inline content at the start of the code, which is together with block-level content in the same parent.
  3. B/b of d1
  4. B/b of d2
  5. B/b of i1
  6. B/b of s1
  7. B/b of s2
  8. B/b of s3
  9. B/b, replaced content of i1

E.g. 2:

This example shows a case where a recursive application of the ordering is made for a float.

Code:

<div id = "d1"></div>
<div id = "f1" style = "float:right">
	<div id = "d2"></div>
</div>
<div id = "d3"></div>

Painting Order:

  1. Background(s)/border(s) of parent
  2. B/b of d1
  3. B/b of d3
  4. f1's content, via a recursive application
    1. B/b of f1
    2. B/b of d2

E.g. 3:

This example shows a case where, two levels further into the tree than the starting point, there is an element with a negative z-index. The negative z-index causes it to be painted quite a bit earlier than its sibling.

Code:

<div id = "d1"></div>
<div id = "d2">
	<div id = "ib1" style = "display:inline-block">
		<div id = "d3"></div>
		<div id = "d4" style = "position:relative;z-index:-1"></div>
	</div>
</div>

Painting Order:

  1. Background(s)/border(s) of parent
  2. d4's content, via a recursive application
    1. B/b of d4
  3. B/b of d1
  4. B/b of d2
  5. ib1's content, via a recursive application
    1. B/b of ib1
    2. B/b of d3

E.g. 4:

This example shows the difference between the z-index values of "0" and "auto". Here we assume the parent element is itself an element with an int z-index.

Code:

<div id = "d1" style = "position:relative;z-index:0">
	<div id = "d2" style = "position:relative;z-index:-1"></div>
</div>
<div id = "d3" style = "position:relative;z-index:auto">
	<div id = "d4" style = "position:relative;z-index:-1"></div>
</div>

Painting Order:

  1. Background(s)/border(s) of parent
  2. d4's content, via a recursive application
    1. B/b of d4
  3. d1's content, via a recursive application
    1. B/b of d1
    2. d2's content, via a recursive application
      1. B/b of d2
  4. d3's content, via a recursive application
    1. B/b of d3