Gavin
Gavin

Reputation: 7942

Keeping all elements aligned to the same grid and spanning multiple columns

I'm trying out CSS3's flexbox for the first time and it seems promising, but I'm having some trouble getting it to behave.

Basically, I want flexbox to behave like a table but with an order property so I can tell it in the CSS what order to display the elements in the grid. Is this even possible with flexbox?

Here's the test page I created: https://jsfiddle.net/Lb838dwf/

HTML:

<div id="main">
  <div id="div1">div 1</div>
  <div id="div2">div 2</div>
  <div>generic</div>
  <div id="div3">div 3</div>
  <div>generic</div>
  <div id="div4">div 4</div>
  <div>generic</div>
  <div>generic</div>
  <div>generic</div>
  <div>generic</div>
  <div>generic</div>
</div>

CSS:

#main {
    width: 100%;
    border: 1px solid #c3c3c3;
    display: -webkit-flex;
    display: flex;
    -webkit-flex-wrap: wrap;
    flex-wrap: wrap;
}

#main div {
    min-width: 25%;
    height: 100px;
    -webkit-flex: 1;
    flex: 1;
    -webkit-order: 2;
    order: 1;
    background-color: #ccc;
}

#main #div1 {
    -webkit-order: 3;
    order: 3;
    background-color: coral;
}
#main #div2 {
    -webkit-order: 5;
    order: 5;
    background-color: lightblue;
}
#main #div3 {
    -webkit-flex: 2;
    flex: 2;
    -webkit-order: 2;
    order: 2;
    background-color: lightgreen;
}
#main #div4 {
    -webkit-order: 4;
    order: 4;
    background-color: pink;
}

You can see that #div3 has flex: 2, which means it should span two columns, but it's only taking up 1 column. Also, the 3 divs at the bottom (div1, div4, div2) aren't aligning to the same grid as the items above it. If I add max-width: 25% to the #main div style, it keeps everything to the same grid, but spanning columns doesn't work. I tried setting max-width: 25% for all divs and max-width: none for just #div3 (the one with flex:2) but that doesn't have an affect.

Upvotes: 2

Views: 130

Answers (1)

Michael Benjamin
Michael Benjamin

Reputation: 371231

Let's dissect a few of the things you wrote so we could clarify flexbox behavior.


You can see that #div3 has flex: 2, which means it should span two columns, but it's only taking up 1 column.

flex: 2 does not mean it should span two columns. This isn't like an HTML colspan attribute. (And even if it was, you have three other flex items ["table cells"] already occupying the three remaining columns, so how would #div3 expand two columns? It would have to break out of the grid.)

The flex property is a shorthand for the flex-grow, flex-shrink and flex-basis properties.

The flex-grow property controls how flex items will expand by distributing remaining space in the flex container. So by applying flex: 2 you're saying you want the flex item to take twice as much remaining space than its siblings, not necessarily be double their size.

From the spec, the flex-grow property:

...determines how much the flex item will grow relative to the rest of the flex items in the flex container when positive free space is distributed.

However, since you've given the container (#main) a width: 100%, and each flex item min-width: 25%, there is no remaining space to distribute. So nothing happens.

To illustrate this behavior, change the width of each flex item to 50px. This leaves extra space to distribute and #div3 takes 2x as much. See demo: https://jsfiddle.net/Lb838dwf/6/


Also, the 3 divs at the bottom (div1,div4,div2) aren't aligning to the same grid as the items above it.

Correct. They're not aligning because you applied flex: 1 to them in #main div. This tells them to evenly distribute all remaining space among themselves.


If I add max-width: 25% to the #main div style, it keeps everything to the same grid, but spanning columns doesn't work. I tried setting max-width: 25% for all divs and max-width: none for just #div3 (the one with flex:2) but that doesn't have an affect.

What? You lost me.


I want flexbox to behave like a table but with an order property so I can tell it in the CSS what order to display the elements in the grid. Is this even possible with flexbox?

Yes, it's possible.

HTML

<div id="main">
    <div>div 1</div>
    <div>div 2</div>
    <div>div 3</div>
    <div>div 4</div>
    <div>div 5</div>
    <div>div 6</div>
    <div>div 7</div>
    <div>div 8</div>
    <div>div 9</div>
    <div>div 10</div>
    <div>div 11</div>
</div>

CSS

#main {
    display: flex;
    justify-content: flex-start;
    flex-wrap: wrap;
    width: 100%;
}

#main div {
    flex: 0 0 150px; /* don't grow, don't shrink, stay at 150px */
    height: 100px;
    margin: 5px;
    background-color: #ccc;
    border: 1px dashed black;
}

#main div:nth-child(2)  { order: -4; background-color: lightblue; }
#main div:nth-child(5)  { order: -3; background-color: lightgreen; }
#main div:nth-child(8)  { order: -2; background-color: lightyellow; }
#main div:nth-child(11) { order: -1; background-color: lightpink; }

DEMO: http://jsfiddle.net/Lb838dwf/4/


Spanning multiple columns

As mentioned above, the flex property is a shorthand for the flex-grow, flex-shrink and flex-basis properties.

flex-grow tells flex items how to distribute available space, hence it is not a reliable tool for emulating the HTML colspan attribute. (The space available in the container and double the size of a flex item are unrelated lengths and not necessarily equal.)

However, the flex-basis property, which sets the initial size of a flex item, can be used to make flex items twice as wide, three times as wide, whatever.

In my code above, flex items are set to: flex: 0 0 150px; The third value represents flex-basis. So each box is set to 150px wide.

For a flex item to occupy two columns simply double that value.

Since flex cascades to all divs, we only need to adjust the flex-basis for targeted items.

#main div:nth-child(11) { flex-basis: calc(300px + 10px); } /* factoring in margin space */
#main div:nth-child(7)  { flex-basis: calc(450px + 20px); } /* factoring in margin space */

DEMO: http://jsfiddle.net/Lb838dwf/5/ (expand window for effect)

Upvotes: 4

Related Questions