Sunday, March 13, 2011

How To Attack An Internet Explorer (Win) Display Bug

It's a well-known fact that the Internet Explorer (Win) browser has a wide variety of display bugs. While it may seem satisfying to badmouth that browser, IE is by far the most popular browser on the web, so whining about its drawbacks is not going to help very much.
Sadly, many authors who desire to make the switch to table-less design begin with high hopes, only to have those hopes dashed by what is sometimes known as the "Explorer bug suite." Some of those bugs are not "bugs" at all, but rather misapplications of the W3C standards. Although a full understanding of how IE "gets it wrong" is a full-time job, it is possible to get by with a modicum of bug fixing tools, which is what this article is going to teach you. All ready? Let's go!

Clearing The Decks

Before any bug fixing can be done, it is necessary to make sure that the problem really is a bug, and not just improper coding, or a failure to understand how CSS positioning works. As a primer on bug hunting you should read Debugging CSS, the Easy Way, which details this preliminary phase in detail. It also describes how to create a "minimal test case," which can greatly simplify the issues to be addressed. But just making such a test case is time consuming, and job pressures often prevent people from going this route. Still, it is a highly recommended method to use, and one we put into practice regularly.
Even when a test case is made, it is not always easy to just look and say, "Aha! That's it!" While it's true that having extensive bug knowledge does help in locating a problem, many busy coders don't have time to devote to studying bugs at length.
However, once a minimal test case is prepared, there are usually very few page elements remaining to consider, and it becomes possible to fire "magic bullets" at various elements in an attempt to solve the problem. Be aware that these fixes are useless against any proper browser behaviors that are mistaken for bugs by CSS newcomers. It's up to you to have a firm grasp on the basics of CSS coding. You can read up on the subject in the following articles:
The fixes to be discussed are also worthless against any of the deliberate CSS specification violations that occur in IE/Win. To learn about the major non-bug IE misapplications, read:
One special problem worth pointing out is the way in which floats are "cleared," as described in the Float: The Theory article mentioned above. IE will usually (wrongly) allow a container to enclose a nested float, even when no clearing element is present. This creates the impression that the non-IE browsers are incorrect, when the real offender is IE/Win. Don't be fooled in this way as have so many others.
You may also find useful information on many smaller display problems in the Common Coding Problems series of articles here at CMX. (A search for the terms Common Coding Problems will get you a list of all the articles in the series.) Assuming these issues are well understood or are not relevant to your current problem, let's proceed to look at the bug fixing methods themselves, shall we?

{ position: relative }

This "magic bullet" was the first one discovered, back in December of 2001 by a certain author who's initials happen to be "BJ" (ahem). Anyway, the issue then was a technique developed by Eric Meyer here, which appears to "punch" a box out of the corner of another box.
Eric's method involves floating a box into the upper left corner of a text containing element, and then using 1px negative top and left margins, which has the effect of "pulling" the float up and to the left by 1px. This allows the float to cover, or obscure, the 1px borders on the larger containing box in that area. Then, the float gets a bottom and right border that seems to be a part of the overall border. Neat.
The problem with this method was that Explorer for Windows was failing to obscure the corner of the large element's border properly, ruining the intended "boxpunch" effect. It seems that IE/Win has a problem when an element is negatively margined out of the parent box surrounding it. The portion still within the parent "container" is visible, but any part that goes beyond the inner edge of the container's border just vanishes!
It turns out that applying a simple {position: relative;} to the negatively margined inner box causes the missing portions to magically reappear. Why? Well, moving right along... Seriously, no one knows why this happens, except perhaps the Microsoft engineers, and they aren't talking. Suffice to say it does work.
Soon, this fix was found to work on other IE bugs as well, specifically the Peekaboo Bug in IE6 (look on the third page of the article). We'll get back to the Peekaboo bug in just a little while.
Another primary use for this fix is occasioned when a floated image is in a container and either the container or the floated image has been made "relative." In that case, the floated image can and will "hide" behind any background that may be placed on the outer container element. Removing the relative positioning or making both elements relative will bring the floated image "up front," allowing it to be seen.

Problems With the "Relative" fix

Usually this fix will not adversely affect other browsers, but as just explained, in the case of floated images it may be necessary to perform a "double" fix, using {position: relative;} twice. Figuring out where to apply this fix is not usually a problem. Just stick it on one element at a time and check to see if the bug is zapped. In the event that this fix does cause trouble in other browsers, you may hide this fix from them, feeding it only to IE/Win via the methods outlined in the next section.

Dimensional Bug Fixing

A very large percentage of IE/Win bugs are triggered by the lack of any stated dimensions on elements that contain nested floats. In other words, if you have a box without either a width or a height, and nest one or more floats inside, some very weird display bugs may declare themselves. Such bugs can happen without floats, but are far rarer than the float variety.
The Peekaboo Bug falls into this "dimensional bug" class, as does the Escaping Floats Bug. There's the Peekaboo Bug again! So this hard-shelled devil is vulnerable to both the "relative" fix and the dimensional fix! Very interesting. Such is the nature of IE bug "science."
This sort of dimension-related IE buggy-ness has been known for several years, but simply knowing wasn't good enough. Very often, it is undesirable to apply a dimension to the buggy box. Using a height is usually unacceptable, since having the height be the default value of auto allows the content in the box to determine that box's displayed vertical height. If you give the box a defined height, it may not match the height of the content, particularly if text is involved and the user changes the browser text sizing.
A width may sometimes be used, but often the page design calls for the problem box to be "liquid," so as to automatically widen or narrow in response to changes in window size. Yes, a percentage width can also do this, but what if the box is a center column and needs to have pixel sized side columns next to it? It is impossible to have a percentage width on a box and pixel sized margins on that same box, because no one can predict how rigid pixels may compare against fluid percentages.
Clearly, using width as a bug-fixing dimension is less than satisfactory. There seemed no clean way to accomplish the task at hand, and yet by a strange quirk of fate, the Explorer browser itself came to the rescue.

The Expanding Box Trick

CSS gurus have known for a long time that when IE/Win sees that a box has a stated dimension that is not large enough to enclose the content of that box, the browser will wrongly enlarge the width of the box to accommodate unbreakable lines, such as long URLs. IE/Win will also extend the height of a box so that its content does not overflow the bottom border of the box. Browsers that more closely follow the W3C specifications let the extra content protrude beyond the height or width controlled box.

The Holly Hack

Since IE/Win will misbehave in this manner, when a dimension is needed to fix a float bug, a very small height, such as {height: 1%}, can be applied to the float container, and IE/Win will just make the box taller anyway. Thus, a small height does not change the appearance of the box, but IE seems to think the box is now "dimensioned," and so our magic bullet strikes home. Scratch one bug.

Details of The Hack

Since other browsers don't have the bug, and more importantly, don't wrongly expand height-defined boxes, this bug-fixing height should be hidden from browsers other than IE/Win. It is not necessary to hide this fix from earlier versions of IE/Win, since even though they don't have the Peekaboo Bug, they do have other dimension based bugs, and they all do the expansion trick. However, IE5/Mac doesn't incorrectly expand boxes, so letting that browser see the small height is sure to damage its display. Thus, the height needs to be hidden from it, as well as the other, non-IE browsers.
The following code is an instance of the Holly hack. Primarily, the Holly hack consists of a set of hiding methods wrapped around that 1% height, which is the key to the hack. Remember, the whole idea is to let IE/Win and only IE/Win see that height and apply it to the buggy box.
/* Hides from IE5-mac \*/
* html .buggybox {height: 1%;}
/* End hide from IE5-mac */
Code Block 1
Two Hacks in One: The first line of code is a CSS comment with an "escape" (colored red) just before the comment closing tag. The presence of that escape character causes IE5/Mac to ignore the closing tag of the comment, and think that the comment is still active. Thus, it ignores everything until it sees what it thinks is a correct CSS comment closing tag. The last line of the hack is a normal CSS comment, and its closing tag is what lets IE5/Mac begin parsing code again. We generally refer to these two lines (the top and bottom in the above code box) as the "Mac-hack," but it is also referred to as a "comment-backslash hack."
The second, or middle line, has, in this example, three items separated by descendant combinators, which are the spaces you can see, and then the height property within curly brackets. First item is a universal selector or star, *, which selects any element, followed by html (both also in red), followed by the element suspected to have the bug. The above code selects an element with the class attribute of buggybox that is a descendant of an html element, (which will always be true), when html itself is a descendant of any element (*), and applies a {height: 1%} to that element, in this case, .buggybox.
It so happens that all IE browsers, both Win and Mac, recognize an invisible and mysterious wrapper element around <html>. The use of the universal selector preceding html, also known as the Tan Hack (or Star HTML Selector Bug), works in IE browsers and nowhere else. Thus, the reason for using the "Mac-hack" is to prevent IE5/Mac from seeing this height just as other browsers are, because it does not wrongly enlarge the box like IE/Win, but it does read the Tan hack.
Note that the element to be given the height need not be classed. Any valid selector may be inserted instead of .buggybox, and even entire strings using descendant combinators may be used there. What really matters is that * html must always precede whatever target element is to be fixed, and that the * and html have a space between them, followed by another space, and then the target element.
There are other methods that can be employed to both give IE/Win the height-fix it needs, and not give it to other browsers. One method is to set the small height in the regular selector for the element and then override that height with the auto value in a following selector that uses a child combinator, >, which IE/Win browsers do not recognize. This requires knowing the direct parent of the buggy box. It is not a good idea to use spaces around the child combinator, as this can cause bugs.
Another method is to use the Tan hack to hide the height from browsers other than IE, and then override that height for IE5/Mac only, using a modified Mac-version of the Tan hack, which again uses the child combinator to prevent IE/Win from reading the redefined value. The Mac-version also requires an escape character (a backslash, \) to be used in each property name included in a ruleset to prevent IE5/Win from reading the rules. In the Mac-modified version of the Tan hack, the child combinator replaces the descendant combinator normally used between the star and html. This is shown in red in the last line below. The escape (the backslash character, also in red) is used in the height property. Both of the alternate methods just described are shown in the following code block.
Child Combinator Override Method
.buggybox {height: 1%;}
#parentElement>.buggybox {height: auto;}

Mac-modified Tan Hack Method
* html .buggybox {height: 1%;} /* for IE/Win */
*>html .buggybox {he\ight: auto;} /* for IE5/Mac */
Code Block 2
Which method should you choose? Well, as with most everything CSS, it depends. Of the three options presented, the one we would be least likely to use is the Child Combinator Override Method. We prefer to have the original selector for an element contain the correct value for the properties defined within, and use a hack, or filter, as they are sometimes called, to provide alternate values for IE/Win. The other two methods can come down to a matter of preference.
Note: The Child Combinator Override Method is our name for the method described in Code Block 2 above. As far as we know, there is currently no commonly used name for this method, or the one using the Mac-modified version of the Tan hack. Also be aware that if a future version of IE/Win finally does support the child combinator, this hack may backfire by letting IE/Win see the "wrong" height value, depending on what changes actually occur in that browser.

Trick Shooting

While having a "minimal test case" to try these fixes on is the best way, it is sometimes possible for a CSS coder to skip that step and just apply them directly to parts of a big convoluted page in the hope of "getting lucky." This approach can be problematic for newbies however, because what may look like a bug is often actually correct behavior, or an IE/Win specification violation. A good rule of thumb is to compare the display of a suspected IE/Win bug to the display in Opera 7 and a Mozilla based browser. If both these non-IE browsers look good then it's a fair bet you are seeing an IE bug or misbehavior. Telling these apart is another matter, requiring knowledge of the IE Float model, the IE Box model, the IE 3px problem, and occasionally other issues as well.
These two "magic bullets" can work wonders, enabling authors to utilize many layouts previously out of reach due to IE bugs. They are by no means cure-alls, though they may seem as such at times, and they are certainly no substitute for good coding practices. While there are other fixes not detailed here, the ones we have described are the primary weapons in use today for zapping bugs in complex table-less designs. One recent and very tricky float layout employs as many as eight Holly hacks to force compliance from "that" browser. Bang! Bang!

No comments:

Post a Comment