Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional multiple columns in CSS

Tags:

css

I have a menu containing some sub-menus, like this one

.menu {
  position: absolute;
  background: yellow;
  margin-top: 2em;
}
header {
  font-size: 20px;
}
ul {
  -moz-column-count: 2;
  -moz-column-gap: 10px;
  -webkit-column-count: 2;
  -webkit-column-gap: 20px;
  column-count: 2;
  column-gap: 10px;
  list-style: none;
  padding: 0;
}
li:nth-child(odd) {background: lightblue; }
li:nth-child(even) {background: lightyellow; }
div:not(.main) {
  display: inline-block;
  position: relative;
}
<div>menu one
  <div class="menu">
    <header>menu one header</header>
    <ul>
      <li>Item one</li>
      <li>Item two 2</li>
      <li>Item tree</li>
      <li>Item four</li>
      <li>Item five</li>
      <li>six</li>
      <li>Item 7</li>
    </ul>
  </div>
</div>
<div>menu two</div>

I need that the 2 columns appears ONLY if there are more than 4 entries in the .menu>ul list.

Is it possible using the pure CSS?

like image 462
serge Avatar asked Sep 25 '15 14:09

serge


3 Answers

You could make use of the trick described here - http://alistapart.com/article/quantity-queries-for-css, which has been suggested by @Jesse Kernaghan in the comments above.

However, to be able to do that you need to make a couple of things hard-wired:

  1. Fix the height and width of the container ul, and
  2. Fix the height of the li and ensure that this width times n equals the height of the container ul. Where n is your magic number, in your case 4.

This is required so that the CSS columns does not reflow the content and that the li are arranged into columns in a predictable fashion.

If your markup and use-case can work with the above two constraints, then you can use the following CSS selectors:

li:nth-last-child(4):first-child, 
li:nth-last-child(4):first-child ~ li {
  width: 160px;
}

The first selector will select the li which is 4th from the end and also happens to be the first, effectively selecting only the first li in a set of exactly four lis, where 4 being your magic number.

The second selector (the general sibling combinator) will select all the li that exist after the first one.

Apply a fixed width to these lis to give a faux appearance of one column.

Example Snippet:

div.menu {
  display: inline-block; vertical-align: top;
  background: yellow; margin: 2em 1em;
}
header { font-size: 20px; }
ul {
  width: 160px; -moz-columns: 2 auto; -moz-column-fill: auto;
  -webkit-columns: 2 auto; columns: 2 auto;
  list-style: none; padding: 0; height: 8em;
}
li { height: 2em; }
li:nth-child(odd) {background: lightblue; }
li:nth-child(even) {background: lightyellow; }

li:nth-last-child(4):first-child, li:nth-last-child(4):first-child ~ li {
  width: 160px;
}
<div class="menu">
  <header>menu one</header>
  <ul>
    <li>Item one</li>
    <li>Item two 2</li>
    <li>Item tree</li>
    <li>Item four</li>
    <li>Item five</li>
    <li>six</li>
    <li>Item 7</li>
  </ul>
</div>
<div class="menu">
  <header>menu two</header>
  <ul>
    <li>Item one</li>
    <li>Item two 2</li>
    <li>Item tree</li>
    <li>Item four</li>
  </ul>
</div>
<div class="menu">
  <header>menu three</header>
  <ul>
    <li>Item one</li>
    <li>Item two 2</li>
    <li>Item tree</li>
    <li>Item four</li>
    <li>Item five</li>
  </ul>
</div>
like image 101
Abhitalks Avatar answered Oct 18 '22 20:10

Abhitalks


Here is one way, tested with Chrome, FF, IE11 and Edge, though still with some fixed value, and need to be adjusted if more than 8 items.

The li height need to be fixed and then the negative margin-top will be 4 times that height to push the right items to the top.

The trick here is to have each item wider than 50% of the available width when 2 "columns" is needed, so the float's stay on its side and then push the right "column" up so it aligns with the left.

.menu {
  position: absolute;
  background: yellow;
  margin-top: 2em;
}
header {
  font-size: 20px;
}
ul {
  list-style: none;
  padding: 0;
}
li:nth-child(odd) {background: lightblue; }
li:nth-child(even) {background: lightyellow; }

li { position: relative; height: 20px; }
li:nth-child(-n+4) { float: left; width: 48%; margin-right: 3%; }
li:nth-child(n+5) { float: right; width: 48%; margin-left: 3%; top: -80px }

li:nth-last-child(4):first-child, li:nth-last-child(4):first-child ~ li {
  width: 94%; margin-right: 6%
}

div:not(.main) {
  display: inline-block;
  position: relative;
}
<div>menu one
  <div class="menu">
    <header>menu one header</header>
    <ul>
      <li>Item one</li>
      <li>Item two 2</li>
      <li>Item tree</li>
      <li>Item four</li>
      <li>Item five</li>
      <li>six</li>
      <li>Item 7</li>
    </ul>
  </div>
</div>
<div>menu two
  <div class="menu">
    <header>menu two header</header>
    <ul>
      <li>Item one</li>
      <li>Item two 2</li>
      <li>Item tree</li>
      <li>Item four</li>
    </ul>
  </div>
</div>

Update Here is another way, with flex.

Based on the li height and the ul height n items will fit each column.

.menu {
  position: absolute;
  background: yellow;
  margin-top: 2em;
}
header {
  font-size: 20px;
}
ul {
  list-style: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  height: 100px;
}

li {
  height: 22px;
}

li:nth-child(odd) {background: lightblue; }
li:nth-child(even) {background: lightyellow; }

div:not(.main) {
  display: inline-block;
  position: relative;
}
<div>menu one
  <div class="menu">
    <header>menu one header</header>
    <ul>
      <li>Item one</li>
      <li>Item two 2</li>
      <li>Item tree</li>
      <li>Item four</li>
      <li>Item five</li>
      <li>six</li>
      <li>Item 7</li>
    </ul>
  </div>
</div>
<div>menu two
  <div class="menu">
    <header>menu two header</header>
    <ul>
      <li>Item one</li>
      <li>Item two 2</li>
      <li>Item tree</li>
      <li>Item four</li>
    </ul>
  </div>
</div>
<div>menu three
  <div class="menu">
    <header>menu three header</header>
    <ul>
      <li>Item one</li>
      <li>Item two 2</li>
      <li>Item tree</li>
      <li>Item four</li>
      <li>Item five</li>
      <li>Item six</li>
      <li>Item seven</li>
      <li>Item eight</li>
      <li>Item nine</li>
      <li>Item ten</li>
    </ul>
  </div>
</div>
like image 33
Asons Avatar answered Oct 18 '22 21:10

Asons


We can do with quantity query, only there is a order problem. If you compromise with that order we can use quantity query.

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

li {
  width: 40%;
  border: 1px solid red;
  margin: 2px;
}

ul li:nth-last-child(n+5),
ul li:nth-last-child(n+5)~li {
  float: left;
}
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
</ul>

View on jsFiddle

like image 1
Kundan Sankhe Avatar answered Oct 18 '22 19:10

Kundan Sankhe