Friday, April 27, 2007

IE inherited margin bug: form elements and hasLayout


Internet Explorer on Windows has a wide variety of rendering bugs.
Many of these bugs can be worked around by giving affected elements
the IE-specific "hasLayout" property and an
extensive
analysis of how and when this works
is available. This article
describes the opposite situation: an IE rendering bug that is
triggered by elements having layout for which the workaround is to
remove the hasLayout property.

The bug is that certain form input elements whose containing box
has layout inherit the sum of the margins of all of their containing
boxes. Unlike the the well known
IE
doubled float-margin bug
, this one does not involve floats. There
is a workaround but, so far, no magic-bullet CSS-only
solution.

The Problem

Consider this sample code:

.has-margins {
background-color: gray;
margin-left: 5em;
padding: 1em;
}

.has-layout {
background-color: tan;
width: 30em;
}

<form action="ignore">
<div class="has-margins">
<div class="has-layout">
INPUT tag, type=text:<br>
<input type="text"><br>
INPUT tag, type=checkbox:<br>
<input type="checkbox">
</div>
</div>
</form>


The outer has-margins box is gray and the full width of the page
except for a left margin of 5em. The inner has-layout box is tan,
30ems wide, and contains four lines of content: two of text and two
form elements. All four lines of content should be flush against the
left edge of the tan box.

Here is how it actually displays:

INPUT tag, type=text:



INPUT tag, type=checkbox:



For all CSS-enabled versions of IE/Win, including IE7, the
left edge of the text input field (the second line) is pushed
to the left. It incorrectly inherits the left margin of its
parent box because the parent has a width value, giving it
hasLayout.

Consider another example which wraps a second has-margins box
around the inner has-layout box:


<form action="ignore">
<div class="has-margins">
<div class="has-margins">
<div class="has-layout">
INPUT tag, type=text:<br>
<input type="text"><br>
INPUT tag, type=checkbox:<br>
<input type="checkbox">
</div>
</div>
</div>
</form>

INPUT tag, type=text:



INPUT tag, type=checkbox:



The left edge of the has-layout box is correctly pushed twice as
far to the left (since both enclosing has-margins boxes have a left
margin). However, the affected input is now also pushed twice as far
to the left; compare its location to the text field in the previous
example. The form element actually inherits the the sum of
the left margins of all of its ancestors. Interestingly, it does not
inherit the top or bottom margins.

The bug seems to occur for all INPUT types except checkbox, radio,
and image as well as TEXTAREA elements but not for SELECT
elements.


Workarounds


The problem only seems to occur when the direct parent of the INPUT
element has hasLayout and any ancestor has margins. This leads to
several possible workarounds:



  • Set a negative margin on the INPUT element equal to the sum of all
    of its parents' margins. This is fine for static sites but if you are
    using a CMS it is probably not possible without scripting. Keep in
    mind that only some INPUT element types are affected; assigning
    negative margins to checkbox, radio, or image inputs will break your
    layout.


  • Remove the margins from all ancestor elements. This is not
    particularly realistic.


  • Remove the hasLayout-granting style from the containing DIV.
    Since you presumably put that style there for a reason, this is also
    not particularly realistic.


  • Put inline text, a LABEL, or possibly any inline element at all
    immediately before the INPUT element. The fact that this solves the
    problem is probably why some people have not noticed it. Example:

    <form action="ignore">
    <div class="has-margins">
    <div class="has-layout">
    INPUT tag, type=text, preceeded by inline text:<br>
    Text: <input type="text"><br>
    </div>
    </div>
    </form>

    INPUT tag, type=text, preceeded by inline text:

    Text:


    Wrap the INPUT element in an unstyled SPAN, LABEL, or, in fact,
    any container element without a hasLayout-granting style. Example:
    <form action="ignore">

    <div class="has-margins">

    <div class="has-layout">
    INPUT tag, type=text, wrapped in an unstyled SPAN:<br>
    <span><input type="text"></span><br>

    </div>

    </div>

    </form>


    INPUT tag, type=text, wrapped in an unstyled SPAN:



No comments: