Space between list items in IE

This article was originally published 13 April, 2005, when the concept of IE's hasLayout was just beginning to be studied. MSFT did not publically document the property to any great extent, as it was meant to be an internal thing. Unfortunately for us, the issue of hasLayout affected IE's rendering to the extent the developer community, at least those who cared about cross browser compatibility, has spent entirely too much time with its vagaries.
CSSCreator

IE7 has apparently fixed this particular bug. This article will apply to legacy IE6 installations.

There are cases in which IE treats the white space "\n" between </li> and the next <li> as a literal linefeed. That creates extra space between list items. So this code,

<ul>
  <li>list item</li>
  <li>list item</li>
  <li>list item</li>
  <li>list item</li>
</ul>

which should give you this,

list item
list item
list item
list item,

instead gives this,

list item

list item

list item

list item

The common fix has been to remove the offending white space;

<ul><li>
  list item</li><li>
  list item</li><li>
  list item</li><li>
  list item</li>
</ul>

<ul><
  li>list item</li><
  li>list item</li><
  li>list item</li><
  li>list item</li><
/ul>

<ul><li>list item</li><!--
--><li>list item</li><!--
--><li>list item</li><!--
--><li>list item</li><!--
--></ul>

That works, but is ugly. In the case of non-trivial lists, the formatting can make the list code all but unreadable. Plus, jiggering the html formatting to control display is just wrong.

I thought I had found the secret to the IE white space bug in lists, but there were 'issues'. A bulleted list had screwy placement of the bullets. As long as you don't need the bullets, all is well. I don't think that little caveat makes for a good cure. So the search was restarted.

I figured I had discovered the trigger to be whether the list item was dimensioned, eg. the width set. From that I figured MSIE's hasLayout was involved. There was another clue, and I glommed onto the wrong one. The trigger is actually a link with {display: block;}1. You may see this in the following test cases;

  1. <ul id="plain">
      <li>list item</li>
      <li>list item</li>
      <li>list item</li>
      <li>list item</li>
    </ul>
    
  2. <ul id="inline-links">
      <li><a href="#">some inline link</a></li>
      <li><a href="#">some link</a></li>
      <li><a href="#">some link</a></li>
      <li><a href="#">some link</a></li>
    </ul>
    
  3. #block-links a {
      display: block;
      }
    ==========
    <ul id="block-links">
      <li><a href="#">some block link</a></li>
      <li><a href="#">some link</a></li>
      <li><a href="#">some link</a></li>
      <li><a href="#">some link</a> </li>
    </ul>
    

Only #3 will exhibit the white space bug. So an undimensioned <li> is not the prime mover. Will setting a dimension do away with the extra space? Yes. That only corroborates the evidence that this is a hasLayout related 'feature'.

Here's a live example (view in IE6):

* html #hacked-li li {
  height: 1px;
  }
==========
<ul id="hacked-li">
  <li><a href="#">some block link</a> hacked li</li>
  <li><a href="#">some link</a></li>
  <li><a href="#">some link</a> </li>
  <li><a href="#">some link</a></li>
</ul>

Notice the bullets. They go to the bottom of the list item. They're too low on a single line, and opposite the last line on multi-line list items. I used the Holly hack to trigger hasLayout, but all methods do the same or worse.

Properties and corresponding values that, if set, cause an element to have layout2.

      CSS property: Value
      --------------------
      display:      inline-block
      height:       any value
      float:        left or right
     *max-height:   any value
     *max-width:    any value
     *min-height:   any value
     *min-width:    any value
     *overflow:     not visible
      position:     absolute
      width:        any value
      writing-mode: tb-rl
      zoom:         any value

     *added with IE7

The hasLayout property applied to the <li> fixes the white space bug but causes its own set of problems. MS says hasLayout applies to anchors, too. Since the anchor caused the problem, can it fix it?

Live example, view in IE6.

* html #hacked-link a {
  height: 1px;
  }
==========
<ul id="hacked-link">
  <li><a href="#">some block link</a>hacked link</li>
  <li><a href="#">some link</a> </li>
  <li><a href="#">some link</a></li>
  <li><a href="#">some link</a> </li>
</ul>

Why, yes. Yes, it does, and without the shifted bullets. I think I can now declare a general fix for the IE white space bug. Cause the anchor element to have hasLayout=true. The full test case5 should bear me out.

All that, and I did run into another fairly unknown fix3. Give the <li> a bottom border! How simple is that? It has limitations on where it can be used, but when it can …

And yet another fix is found4. This has already been mentioned, {display: inline-block;}, as setting hasLayout=true. What is new is that we can use that as a trigger, then go to {display: block;} without un-setting hasLayout. So, doing The following will set hasLayout, but the {display: block;} will apply as overruling the earlier display value.

a {
  display: inline-block;
  }

a {
  display: block;
  }