41

I have an "Ordered List" which contains about 100 "List Items". This ol makes my page very long and users have to scroll too much.

How can I get the UL to show like this:

1.           6.           11.
2.           7.           12.
3.           8.           13.
4.           9.           14.
5.          10.           15.
TylerH
  • 20,799
  • 66
  • 75
  • 101
anonymous
  • 2,294
  • 5
  • 23
  • 27
  • There was [an article on A List Apart](http://www.alistapart.com/articles/multicolumnlists/) a while back which covered exactly this question. I guess if what is mentioned there doesn't suffice you could of course always revert to server-sided coding or client-side coding to divide the list automatically in three portions. – Michiel Feb 27 '10 at 14:22

8 Answers8

33

If for you don't care about the vertical order, but only the layout:

1.      2.      3.       4.
5.      6.      7.       8.
9.      10.     11.      12.

You can simply set the li elements this way:

li {
    display: block;
    width: 25%;
    float: left;
}

It should work. If you need to have them in vertical order you need to act in the php script dividing them into separate divs and then float them.

Enrico Carlesso
  • 6,818
  • 4
  • 34
  • 42
  • is there anyway I can get them in vertical order using css/html? without having to break them up? – anonymous Feb 27 '10 at 11:14
  • 1
    As far as I know, no. As mentioned, when css3 column mode will be supported it will be an easy task, but every trick I'm wondering needs to break-up the page in some way. – Enrico Carlesso Feb 27 '10 at 13:04
  • 1
    it seems to have an issue if for example, item 3 is 2 lines tall, and items 4 is 5 lines tall, etc, then there will be big gaps between item 1 and item 5, while the vertical arrangement can have always 1 empty line between item 1 and item 5 – nonopolarity Aug 07 '13 at 22:47
20

You could use the CSS multi-column layout module. You can check support at Caniuse.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Knu
  • 14,806
  • 5
  • 56
  • 89
  • 1
    could you please provide working example using css3 column module. – Santosh Apr 23 '14 at 13:35
  • My problem with the css3 columns is they do not align to top very well. I ended up doing it with jquery: http://jsfiddle.net/EebVF/5 Using this jquery plugin: http://github.com/fzondlo/jquery-columns – newUserNameHere May 03 '14 at 14:10
5

I was able to get the proper ordering with a little jQuery:

function splitList($list, n) {
    var i, k;
    var colHeight = Math.ceil($list.children().length / n)
    var colWidth = Math.floor(100 / n) + "%"

    for (i = 0; i < n; i++) {
        $list.append("<ul class='liCol'></ul>")
        for (k = 0; k < colHeight; k++) {
            $list.children("li").eq(0).appendTo(".liCol:last")          
        }   
    }

    $(".liCol").css("width", colWidth)
    $list.show() // list originally hidden to avoid displaying before ready
}

basic styles for .liCol:

.liCol {
    padding: 0px;
    margin: 0px;
    float: left;
}
blocks
  • 908
  • 2
  • 7
  • 14
  • This is a great solution, especially for a CMS where you might not want to hardcode the menu markup (to allow the end user to add more links to the menu later). – deweydb Apr 17 '13 at 15:38
3

If you use CSS multi-column layout, you can do it this way:

ul {
    list-style-type: none;
    counter-reset: section;
    -moz-column-count: 3;
    -moz-column-gap: 20px;
    -webkit-column-count: 3;
    -webkit-column-gap: 20px;
    column-count: 3;
    column-gap: 20px;
}

ul li {
    padding-left: 30px;
    position: relative;
}

ul li:before {
    counter-increment: section;
    content: counter(section) ".";
    margin: 0 0 0 -34px;
    text-align: right;
    width: 2em;
    display: inline-block;
    position: absolute;
    height: 100%;
}
<ul>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
</ul>

JSFiddle demo

TylerH
  • 20,799
  • 66
  • 75
  • 101
VoVaVc
  • 771
  • 1
  • 13
  • 31
  • Support is much wider nowadays[http://caniuse.com/#feat=multicolumn] and old browsers will still display the list. In fact you could combine this technique with this other one [http://stackoverflow.com/a/2347094/2817112] for a nice fallback. – Oriol Mar 02 '15 at 21:23
1

I created a solution that also works for ordered (numbered) lists. These lists have to continue numbering through the columns.

Add the following script to your page (doesn't matter where, nicest is in a seperate js-file):

<script type="text/javascript">
// As soon as the document's structure has been loaded:
document.addEventListener( "DOMContentLoaded", function() {
    // For each html elem:
    elems = document.getElementsByTagName("*"); // OL and UL wanted: chose all (*) here and select later.
    for ( var e = 0; e < elems.length; e++ ) {
        // Check if elem is a list (ordered/unordered) and has class name "cols":
        if ( ( elems[e].tagName == "OL" || elems[e].tagName == "UL" ) && elems[e].className.search("cols") > -1 ) {
            // Collect list items and number of columns (from the rel attribute):
            var list = elems[e];
            var listItems = list.getElementsByTagName("LI");
            var Ncols = list.getAttribute("rel")*1; // *1 converts rel from string to int.
            // Determine total number of items, items per column and column width:
            var Ntotal = listItems.length;
            var Npercol = Math.ceil( Ntotal/Ncols );
            var colWidth = Math.floor( 100/Ncols )+"%";
            // For each column:
            for ( var c = 0; c < Ncols; c++ ) {
                // Create column div:
                var colDiv = document.createElement("DIV");
                colDiv.style.cssFloat = "left";
                colDiv.style.width = colWidth;
                // Add list items to column div:
                var i_start = c*Npercol;
                var i_end = Math.min( (c+1)*Npercol, Ntotal );
                for ( var i = i_start; i < i_end; i++ )
                    colDiv.appendChild( listItems[0] ); // Using listItems[0] instead of listItems[i], because items are moved to colDiv!
                // Add column div to list:  
                list.appendChild( colDiv );
            }
        }
    }
} );
</script>

Then you can simply create multiple columns lists like this:

<ol class="cols" rel="3">
    <li>A</li>
    <li>B</li>
    <li>C</li>
    <li>D</li>
    <li>E</li>
    <li>F</li>
    <li>G</li>
</ol>

So, setting class="cols" and rel="[number_of_columns]" and the script will do the rest!

Herman
  • 361
  • 3
  • 13
0

You can use 2D transforms: they have a wider support by modern browser than CSS3 columns. See my answer here

2 row element layout within horizontal div

Community
  • 1
  • 1
Fabrizio Calderan
  • 120,726
  • 26
  • 164
  • 177
-1

Since I had the same problem and couldn't find anything "clean" I thought I'd posted my solution. In this example I use a reversed while loop so I can use splice instead of slice. The advantage now is splice() only needs an index and a range where slice() needs an index and the total. The latter tends to become difficult while looping.

Disadvantage is I need to reverse the stack while appending.

Example:

cols = 4; liCount = 35

for loop with slice = [0, 9]; [9, 18]; [18, 27]; [27, 35]

reversed while with splice = [27, 8]; [18, 9]; [9, 9]; [0, 9]

Code:

// @param (list): a jquery ul object
// @param (cols): amount of requested columns
function multiColumn (list, cols) {
    var children = list.children(),
        target = list.parent(),
        liCount = children.length,
        newUl = $("<ul />").addClass(list.prop("class")),
        newItems,
        avg = Math.floor(liCount / cols),
        rest = liCount % cols,
        take,
        stack = [];

    while (cols--) {
        take = rest > cols ? (avg + 1) : avg;
        liCount -= take;

        newItems = children.splice(liCount, take);
        stack.push(newUl.clone().append(newItems));
    }

    target.append(stack.reverse());
    list.remove();
}
Tim Vermaelen
  • 6,869
  • 1
  • 25
  • 39
-1

So I've looked into this and found a solution that will work for all browsers.

My HTML list:

<ol class="list-items">
    <li>Item1</li>
    <li>Item2</li>
    <li>Item3</li>
    <li>Item4</li>
    <li class="second-list">Item5</li>
    <li class="second-list">Item6</li>
    <li class="second-list">Item7</li>
    <li class="second-list">Item8</li>
</ol>

Notice I gave the last 4 list items a class of second-list, this is important for our jQuery.

Next, on my webpage, I made a div with class second-list-appender in the second column, the column I want my second list to be in.

var secondListStart = $(".list-items").children().length - 3;

$(".second-list-appender").append($("<ol start=" + secondListStart + ">"));
$(".second-list-appender ol").append($(".second-list"));
$(".second-list-appender").append($("</ol>"));

See, I actually make 2 lists out of 1, but make it look like one list in 2 columns, by giving start of the second list the next number of the previous list.

I did it with 2 columns, but you can just repeat this process, or create a function and call that within a loop for how many times you want to repeat it.

TylerH
  • 20,799
  • 66
  • 75
  • 101