开发者

Nested list with left aligned "bullets"

I have a requirement to display a hierarchy of projects. The obvious solution is a series of nested unordered lists. However, the problem I need to overcome is that the node value needs to b开发者_Python百科e left aligned across the entire tree with the project name being indented as expected in a tree. Here is a sample

  Project Root Node
1    Task 1
1.1     Travel
1.2     Do Work
2    Task 2
2.1     Perform Testing
2.1.1      UI Testing
2.1.2      Connection Testing

The markup is fairly simple...

<ul>
   <li><span></span>Project Root Node
      <ul>
         <li><span>1</span>Task 1
            <ul>
               <li><span>1.1</span>Travel</li>
               <li><span>1.2</span>Do Work</li>
            </ul>
         </li>
         ...and so on...

Turning the bullets off is easy. Left aligning every li is easy. But the problem is getting the span to stay to the left while the text after the span is properly indented and aligned for that level.

The closest I could get it was that since the span for each level has the same number of characters I could just add a right margin and that would push the following text over. The problem is that since the characters in the span vary slightly in width, the text could be one or two pixels off in vertical alignment with the row above or below.

The other solution I had was to include a "level number" class when rendering the li element along with an appropriate style that set inline-block and width. My problem with that is making sure I defined enough levels to cover any tree depth.


an ordered list with a bit of counters magic, no IE7 and below, though it degrades to an indented numbered list.

CSS

ol {
  counter-reset: item; 
  padding: 0; 
  margin: 0;
  margin-left: 20px !ie7;
}
ul {margin: 0; padding: 0; list-style: none;}
li {
  display: block;
}
ol li:before {
  display: inline-block; 
  content: counters(item, ".") " "; 
  counter-increment: item;
  width: 50px;
}
ol li li:before {width: 70px;}
ol li li li:before {width: 90px;}
ol li li li li:before {width: 110px;}

and the HTML:

<ul>
   <li>Project Root Node
   <ol>
   <li>Task 1
      <ol>
      <li>Travel (1.1)
        <ol>
        <li>Travel (1.1.1)</li>
        <li>Do Work (1.1.2)</li>
        </ol>
      </li>
      <li>Do Work (1.2)</li>
      </ol>
    </li>
    <li>Task 2
      <ol>
      <li>Travel (2.1)</li>
      <li>Do Work (2.2)</li>
      </ol>
    </li>
       <li>Task 3
      <ol>
      <li>Travel (3.1)</li>
      <li>Do Work (3.2)</li>
      </ol>
    </li>
  </ol>
</li>   
</ul>

adjust the widths of the :before psuedo elements to create the indented text

updated to make project node not numbered and include IE7 remargin

UPDATE

link to JSFiddle containing a samle of code using dls (definition lists) instead bearing in mind the need for IE7 support

JSFiddle using DL


This might be a little "hackish," but what I would do is use the nested unordered lists, as you have them, and place the spans in there as well with classes like list-level-1, list-level-2, and so on. For JavaScript-disabled browsers, it'll display as a normal nested list. Using jQuery or another JavaScript library, convert the lists into series of <div>s (with two classes like list-item-number and list-item-level-1, list-item-level-2). Set the margin-left for the latter to appropriate widths.

<div class="row"><div class="list-item-number">1</div><div class="list-item-level-1">Fruits</div></div>
<div class="row"><div class="list-item-number">1.1</div><div class="list-item-level-2">Apples</div>
<div class="row"><div class="list-item-number">1.1.1</div><div class="list-item-level-3">Red Delicious</div>
<div class="row"><div class="list-item-number">1.1.2</div><div class="list-item-level-3">Fuji</div>

You could even use a table to a similar effect (and superior semantic meaning, in my opinion), but I have had bad luck with tables and jQuery.

For a pure HTML/CSS solution, your best option is to use a table. Remember that tables aren't inherently bad -- they're just bad for layout.


To keep everything aligned correctly, I would take the spans out of normal flow by floating them and give them a negative margin. This will cause them not to affect the layout of the text unless they actually run into it. You'll need a rule for each level of the list, but that's just kind of endemic to nested lists in CSS*.

ul {
  padding: 0;
  margin: 0;
  list-style-type: none;
}

li {
  margin-left:1em;
  padding-left: 0;
}

li li span {
  display: block;
  float: left;
  margin-left: -2em;
}

li li li span { margin-left: -3em; }

I just tested it in the horribly broken IE 5 for Mac and it worked perfectly, so I assume newer versions should be OK, unlike solutions using pseudo-elements and generated content (which are stylistically superior but break in Explorer <8).

* Repetitive CSS like this can be trivially generated — a quick Ruby one-liner would be (2..10).each {|n| puts("#{'li '*n}span {margin-left: -#{n}em}")}, with 10 replaced by the number of levels

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜