Splitting list of items in two columns with CSS
Asked Answered
O

3

8

I have a list of items (fetched from CMS) whose number is unknown. I need to display this list in two columns of roughly equal number of items. The following code works but I explain below why it's problematical:

<ul>
    <li>content</li>
    <li>content</li>
    <li>content</li>
    <li>content</li>
    <li>content</li>
</ul>

ul {
  display: flex;
  flex-wrap: wrap;
}

It's all good if the items' content is always the same height. But when one item has a much longer content in it, the sibling item on the same row is forced to expand to match the same height. I don't want that. I want the two columns to be completely independent in terms of items' height.

Possible with CSS alone?

EDIT:

Here is a JSfiddle exposing the problem: https://jsfiddle.net/g9p3m2dp/

Operon answered 1/5, 2017 at 21:19 Comment(0)
J
15

ul {
  -webkit-columns: 2;
     -moz-columns: 2;
          columns: 2;
  padding-left: 0;
}
ul li {
  list-style-position: inside;
  -webkit-column-break-inside: avoid;
            page-break-inside: avoid;
                 break-inside: avoid;
}
<ul>
    <li>content</li>
    <li>content</li>
    <li>content</li>
    <li>content</li>
    <li>content</li>
</ul>

Note columns is actually a shorthand for <'column-width'> || <'column-count'>. This means you can also specify the width (in px, %, em, cm, in...) instead of specifying the number of columns.

Also note you need to set list-style-position: inside; for ol and ul elements to display their children <li>s bullets (or numbers) on the right side column, except for Firefox, which sets list-style-position to inside by default.

Josphinejoss answered 1/5, 2017 at 21:25 Comment(10)
Thanks, it works but there's some weird effect visible at the top edge of second column here: i.imgur.com/TBsoLkW.png. Part of 1st column's last item "bleeds" to the top of second column. Note each item is clickable and expands to reveal a block of text. The problem happens only when I start to click around. Any idea what's going on?Operon
@drake035, whenever you don't want a break to happen inside a child (<li> in your case) you need to specify it, so the breaks only happen between those elements:ul li {break-inside: avoid;}. It too, needs prefixing. Updated answer.Josphinejoss
However, if your elements are expandable, whenever you'll expand one, the ones after it might jump at the start of the next column. In CSS columns the first column is filled first, than 2nd, etc... and the browser will place the break so that the resulting column heights are as close to equal as possible. When adding/modifying items, the browser will re-flow the content.Josphinejoss
Thanks Andrei. So there's really no way to do exactly what I want with CSS alone right? Here's your code running btw: jsfiddle.net/g9p3m2dp/1Operon
@Operon I believe you should be asking this yourself, not me. When an item opens and gets as tall as four other items, what do you want to happen? Do you want the columns to adjust (items to fly over to another column) or do you prefer them to stay put and columns to become unequal? For a) use CSS columns. For b) use two containers and place your items in each container alternatively, until you finish. Each column is on its own and when they have an equal number of items open they are equal in height. Right?Josphinejoss
@drake035, the only bad part about CSS columns is there is no way to animate the fly-over. Here's your updated fiddle, using transitions to open/close children, but as you can see, the column change is instant. In order to animate it, you'd need a plugin that uses absolute positioning, such as masonry, isotope or bootstrap-waterfall.Josphinejoss
I'll go for two containers I guess, thanks a lot for your time and help!!Operon
For some reason, it does not split content in columns.Violone
@SalahAdDin, provide a minimal reproducible example and I might be able to tell you why.Josphinejoss
It just works when the display is blocked.Violone
U
2

You need column-count:

ul {
  column-count: 2;
}
<ul>
    <li>content</li>
    <li>content</li>
    <li>content</li>
    <li>content</li>
    <li>content</li>
</ul>
Upwards answered 1/5, 2017 at 21:26 Comment(7)
Thanks! Same effect happens with your solution as with @Andrei Gheorghiu's solution, please see my comment on his solution.Operon
In this case it would be helpful if you posted your real code, or at least an excerpt that shows the same behaviour (an image isn't enough to find the problem...)Upwards
But I suppose the content of those expanded elements consists of at least three elements, and the last one (empty white space) is moved to the next column. You can try to put a wrapper around each of these elements.Upwards
Seems to be the case actually. But I just spotted another issue: when opening an item on one column, because of its space expansion, one other item of the same column jumps to the other column. That's too weird UX-wise for being OK. Do you think this effect can be avoided somehow?Operon
I really can't say without seeing the actual code in action, or at least part of it which shows the same behaviourUpwards
You're right sorry, I added a Jsfiddle in my question with your code running in it.Operon
re: jumping content between columns: that's regular behaviour in columns. If you don't like it, use "fixed columns", i.e. two DIVs next to each other (50% width each), into which you distribute the content code manually.Upwards
S
0

Maybe you can try this way, but the order will be changed

ul {
  display: block;
}
ul li {
    float: left;
    width: 50%;
}
    <ul>
    <li>content</li>
    <li>content</li>
    <li>content</li>
    <li>content</li>
    <li>content</li>
    </ul>
Susan answered 1/5, 2017 at 21:25 Comment(1)
This solution doesn't fulfill all requirements in my question.Operon

© 2022 - 2024 — McMap. All rights reserved.