buy prednisone

z-index misconceptions, IE bugs and fixes

Update (12/1/2011): Version 1.2

In the past 13 years, I have worked on numerous issues related to layering, specifically the z-index CSS property. It is one of the most misunderstood CSS properties. Surprisingly, there are not many well-written articles or tutorials on this topic.

I came across this blog post that talks about the way to fix a z-index issue. It works as it nails the key point about the parent element, but there isn’t much explanation. You can find all the misconceptions written by other readers.

To understand what z-index is, I find this to be the best beginner’s post. This post went a step further into the concept of “stacking contexts,” which is the major reason of all the bugs you would encounter.

Here are the top two misconceptions:

  • People say… “z-indexes are “absolute”. If an element has a high z-index, e.g. 9999, it should always stay on top of everything.”
    The truth is… WRONG! An element with a z-index would stay below or above other elements in the same stacking context only! That means, if an element with a z-index 9999 that is a descendant of an element that has z-index -1 (negative one), the element is still invisible because ultimately it’s being affected by its ancestor that has a negative one z-index.
  • People say… “without z-indexes declared, the default should always be auto.”
    The truth is… not on IE. If an element has a position CSS property other than the default “static” (i.e. absolute or relative), Internet Explorer is always implicitly giving it a 0 (zero) z-index value. When a bunch of elements with positions come together, whatever appearing later in the document structure will stay on top of elements that appear earlier in the document structure (or the DOM tree).
    (A more technical way to explain this issue is that IE is creating a new stacking context on each of these elements, but it’s less easy to understand so I skip it here and stick with the implicit zero z-index explanation.)

Let’s look at an example:

z-index issue on IE

In this screenshot, the drop-down menu appears below the next gray area. While the drop-down menu has a high z-index value, the two areas (<li> elements) do not have any z-index values! Why the hell would elements with no z-index value cover one with high z-index value?

The bug is caused by the implied zero (0) z-index when position (position: relative in this case) is used. Because the last gray area appears later in the document, it is on top of the second last gray area, even though they have have an implied z-index value of 0. The drop-down menu is a descendant of the parent gray area that has 0 z-index value, so it is eventually covered by the last gray area.

To fix it, you can simply hardcode z-index:1 (or any value greater than 0) to the gray area that contains the drop-down menu, and make sure the bottom gray area has 0 z-index (or no z-index).

In a complex web app or any component-based web design, however, this may not be desired. If you care about the new best practice of object-oriented CSS, you should not hardcode CSS inline, and you should never target a specific instance of the generic CSS class to have a special CSS value.

Because of the reasons above, and maybe you are just lazy to find out what container to apply additional z-index, you can apply this z-index fixer jQuery plugin that I wrote for these issues. What it does is that given a jQuery object, it would go up the DOM to check if any of its ancestors is relatively positioned. Then it adds a high z-index to it to make sure that the the whole family line has high z-index values.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/* Fixes z-index issues - Version 1.2
Update v1.2 - fixed an issue when this is being called on an element that is not in the DOM tree.

This plugin fixes z-index issues typically occur in IE. It traverses up the DOM to make sure that all the ancestors of the target element have a high z-index value.

This fixes a common z-index issue that an element with high z-index is showing underneath its ancestor's sibling that has the same explicit or implicit (e.g. IE's implied zero z-index when position: relative is applied) z-index as the corresponding ancestor.
For IE 6's z-index issues with input boxes, see the jQuery bgiframe plugin.

www.davidtong.me/z-index-misconceptions-bugs-fixes/

param obj:
    recursive: boolean (default: true) - set to false to reduce the number of loops and if it still works, go for false
    exclude: string - a list of class names to be excluded in the fix
    msieOnly: boolean (default: false) - set to true to only apply the fix to IE browsers
    zIndex: string (default: '9999') - a big number that should just be high enough to make the element-to-be-fixed stay on top of other elements
*/

(function($) {
    $.fn.fixZIndex = function(params) {
        params = params || {};
        if (params.msieOnly && !$.browser.msie) return this;
        var num_of_jobj = this.length;
        for (var i = num_of_jobj; i--;) {
            var curr_element = this[i];
            var config_recursive = params.recursive || true;
            var config_exclude = params.exclude || null;
            while (curr_element != document.body) {
                if (!$(curr_element).hasClass(config_exclude) && ($(curr_element).css('position') == 'relative' || $(curr_element).css('position') == 'absolute')) {
                    if ($.data(curr_element, 'zIndex') == undefined) {
                        $.data(curr_element, 'zIndex', curr_element.style.zIndex || '-1');
                    }
                    curr_element.style.zIndex = params.zIndex || '9999';
                }
                curr_element = curr_element.parentNode;
                if (!config_recursive) break;
            }
        }
        return this;
    };

    // optional function to restore z-index if needed
    $.fn.restoreZIndex = function(params) {
        params = params || {};
        if (params.msieOnly && !$.browser.msie) return this;
        var num_of_jobj = this.length;
        for (var i = num_of_jobj; i--;) {
            var curr_element = this[i];
            var config_exclude = params.exclude || null;
            while (curr_element && curr_element != document.body) {
                var currZIndex = $.data(curr_element, 'zIndex');
                if (currZIndex > -1 && !$(curr_element).hasClass(config_exclude)) {
                    curr_element.style.zIndex = currZIndex;
                    $.removeData(curr_element, 'zIndex');
                }
                else if (currZIndex == -1) {
                    curr_element.style.zIndex = '';
                }
                curr_element = curr_element.parentNode;
            }
        }
        return this;
    };
})(jQuery);

Example:

1
2
// .inner_div is covered by other page elements. Use fixZIndex plugin to fix.
$('.inner_div').fixZIndex({recursive: true, msieOnly: true, zIndex: 15, exclude: '.page_container'});

// When we hide .inner_div, restore z-index (optional)
$(‘.inner_div’).restoreZIndex();

Using IE (6, 7 or 8), and open the demo page or see the iframe below. Uncomment the last line in the JavaScript portion to enable the fix.

Note that this plugin does not solve IE6′s bug that form elements always stay on top of other page elements. The solution for that particular bug is at http://brandonaaron.net/code/bgiframe/docs/

Thanks Zak Stein and Adam Platti’s suggestions to put this plugin to work.

You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
6 Responses
  1. Hi David,

    nice article and plugin.

    I’m wondering whether you ever have heard about the z-index issue with Google Earth plugin, which is said one can only have overlay elements using a trick commonly known as iFrame shim.

    http://www.oratransplant.nl/2007/10/26/using-iframe-shim-to-partly-cover-a-java-applet/

    However, this trick seems to be tricky to apply and to work well cross browser with Google Earth plugin:

    https://groups.google.com/group/google-earth-browser-plugin/browse_thread/thread/75a84f0826aeea4b/8bfc71c1c79c32b4?q=shim&lnk=ol&amp;

    Do you think your plugin can help here or do you have maybe a solution to that problem as well?

    Thanks.
    Stephan

  2. David says:

    Hi Stephan,
    I did’t realize this technique is called iFrame shim. The jQuery plugin bgiframe is probably what you need. I’ve also updated my post to include a link to bgiframe.
    Thanks!
    -D

  3. Markus says:

    Hi David,

    thanks for the plugin, it helped me solving ie8 z-index issues with modal windows in ExtJS 3.4. Does your plugin have some kind of license attached to it, or can I re-use it freely (even in a commercial application)?

    regards,
    Markus

  4. David says:

    Go for it bro. You are free to use it however you want. Do let me know if there is any bug or room for improvement though.

  5. Regan says:

    Thanks, David. I’ve been researching this all afternoon looking for a solution that didn’t pertain to the ubiquitous suckerfish dropdown. Your article did the best job of explaining how to work around the bug in the general case. I was hoping against hope for a CSS only solution, but now I understand why I need JavaScript for scalability.

  6. [...] z-index misconceptions, IE bugs and fixes Comment CSS, x-browser [...]

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>