views:

53

answers:

3

There doesn't seem to be an easy way in (well supported) css to do this. I'm looking for a javascript solution, preferably jQuery.

I have an unordered list like this:

<ul>
    <li>A</li>
    <li>B</li>
    <li>C</li>
    <li>D</li>
    <li>E</li>        
    ...etc
</ul>

I want each column to have a height for example four items and fill vertically rather than horizontally like a css float:

A     E
B     F
C
D
+5  A: 

See this article:

One of the minor holy grails of XHTML and CSS is to produce a single, semantically logical ordered list that wraps into vertical columns.

I’ll warn you up front. If you want to present a list in multiple columns, you’ll have to compromise. You can sacrifice W3C web standards and use deprecated markup, you can live with markup that’s less than semantically logical, you can tolerate a mixture of presentation with content, you can say goodbye to browser compatibility, or you can use markup that’s heavy with attributes and styling that’s heavy with rules. Every road exacts a toll.

http://www.alistapart.com/articles/multicolumnlists/

The "best" solution is subjective, but I'd be inclined towards arbitrary classes.

Graphain
I read that before asking here. It's a very good article and I went with a javascript fix because it's not a huge issue if it degrades and I don't want make the html messy.Unfortunately Alist didn't provide any js solution.
Keyo
You're right, good luck with a js solution.
Graphain
+2  A: 

You will want to use a combination of CSS and jQuery, but in theory it is very simple. Render a complete single list in HTML, then provide a wrapper via jQuery and split the list up as desired. The following function does just that. Be sure to use a more specific selector than just ul when actually using the script. An id would be ideal.

View demo here.

jQuery(function ($) {
  var size = 4,
      $ul  = $("ul"),
      $lis = $ul.children().filter(':gt(' + (size - 1) + ')'),
      loop = Math.ceil($lis.length / size),
      i    = 0;

  $ul.css('float', 'left').wrap("<div style='overflow: hidden'></div>");

  for (; i < loop; i = i + 1) {
    $ul = $("<ul />").css('float', 'left').append($lis.slice(i * size, (i * size) + 4)).insertAfter($ul);
  }
});
Doug Neiner
Not quite what I need, but none the less a nice solution. Much simpler than some of the jQuery column plugins.+1 Good work.
Keyo
@Keyo, is the list supposed to fill dynamically?
Doug Neiner
No, but I just don't want to change any of my existing css. I am using a dropdown menu, suckerfish style.By splitting it into 3 lists it would create 3 menu links. I guess it would be invalid to wrap a whole bunch of <li> within a div?My solution below works, but it's not the cleanest I admit.
Keyo
+1  A: 

Doug's solution is nice if you want to split the list up into sub lists.

Instead I chose to position the list elements without changing the dom. This is a bit messy, basically it puts a left margin on each element which is the column number multiplied by the column width. This will result in a staircase layout so the next step was to add some negative top margin to bring each element up to the top.

Basically this displays as a grid. I am using this for drop down menus so it worked well. Avoid using this if you need each list item to have a dynamic height. The col_height variable could be set to the height of the largest item to make the code a bit more general purpose.

var col_max_height = 6; //Max Items per column
var col_width = 200; //Pixels
var col_height = 33; //Pixels
$('.header ul li ul').each(function() {
    $(this).find('li').each(function(index){
        column = parseInt(index/col_max_height);
        $(this).css('margin-left', column * col_width + 'px')
        if(column > 0) {
            $(this).css('margin-top', (index - (col_max_height * column)  + 1) * -col_height + 'px').addClass('col_'+column);
        }
    });
});
Keyo