14

I am trying to layout li's using flexbox. I have them set to column, with 3 li's in each column. The problem is when I want the ul centered.

I am centering the ul using align-content: center. When I do that, and have more li's than the page can show (overflowed), the li's at the beginning get cut off. (The ones on the left side get cut off, but the ones on the right side display fine.)

I will not have a specific number of li's, it could range from 4 to 50. So I therefore cannot remove align-content: center, because when I have a small amount of li's, (let's say 4), the results are not what I want.

How can I center the ul without having it get cut off?

JSFiddle

html, body {
    margin: 0;
    height: 100%;
}
div {
    align-items: center;
    justify-content: center;
    height: 100%;
    display: flex;
    overflow: auto;
    background-color:aqua;
}
ul {
    margin-top: auto;
    margin-bottom: auto;
    flex-direction: column;
    width: 100%;
    height: 70%;
    padding: 0;
    display: inline-flex;
    flex-wrap: wrap;
    align-content: center;
}
li {
    flex-basis: calc(100% / 3 - 2px);
    /* Subtract the border */
    color: firebrick;
    border: 1px solid firebrick;
    background-color: greenYellow;
    list-style-type: none;
    width: 200px;
}
<div>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
        <li>9</li>
        <li>10</li>
        <li>11</li>
        <li>12</li>
        <li>13</li>
        <li>14</li>
        <li>15</li>
        <li>16</li>
        <li>17</li>
        <li>18</li>
        <li>19</li>
        <li>20</li>
    </ul>
</div>
Jessica
  • 9,379
  • 14
  • 65
  • 136
  • jessica, your problem is really cumbersome. But try to use align-content:flex-start; – Shudhansh Shekhar Nov 08 '15 at 05:10
  • That works, but then when there are only 4 `li's` the `ul` isn't centered – Jessica Nov 08 '15 at 05:13
  • ok so you want, if there are less contents, content must be aligned center and when content increases, it must be aligned to left? – Shudhansh Shekhar Nov 08 '15 at 05:15
  • I want it to always be centered. The problem is, when there are a lot of `li's` the content on the left gets cut off – Jessica Nov 08 '15 at 05:18
  • I want the `ul` to be centered. When I use `display:inline-block` for the `ui`, I don't get `flexbox's` layout, which I need. – Jessica Nov 08 '15 at 05:31
  • ok i got your problem. Remove the align-content style and add margin-left:20%; and margin-right:auto; in your style – Shudhansh Shekhar Nov 08 '15 at 05:41
  • This case is problematic because a multicolumn flexbox with `width: auto` doesn't increase its width to cover all columns, only the first one. Then I don't know how to center it properly. I would use JS to count the items and wrap each row in an element, and use `flex-direction: row`. – Oriol Nov 08 '15 at 17:41
  • So you're saying I should have a `ul` for every row? – Jessica Nov 08 '15 at 18:05
  • I am working on this at the moment, but one mistake I found already is that `display: flex` only works **one** level deep, so your **LI**'s are plain child elements of your **UL**, setting flexbox attributes on these **LI**'s have no effect. I missed that one often in the beginning. SO, make your UL `display: flex` and the **DIV** just a generic wrapper. – Rene van der Lende Nov 08 '15 at 22:54
  • **update** I missed the UL inline-flex, sorry about that... – Rene van der Lende Nov 08 '15 at 23:03
  • @Jessica - This doesn't solve your problem, but if you use [`box-sizing: border-box`](https://developer.mozilla.org/en/docs/Web/CSS/box-sizing) then you don't need to use calc with the `flex-basis` to compensate for the border. You can just use 50% and the border (and any padding) is calculated into the width and height: [here is an example](http://jsfiddle.net/m8bykfkr/) – misterManSam Nov 08 '15 at 23:53

4 Answers4

3

How can I center the ul without having it get cut off?

That was a really good question, had some trouble figuring a way to achieve this behavior.

I don't believe you will find a pure CSS solution, however we can do it using a bit of javascript.

Basically i´ve created a script attached to the window resize event that will do the following:

  1. check how many items we have inside our wrapper element: #wrapper
  2. divide the number of elements by 3 (since we have 3 elements in each column) to discover how many columns will be needed to display the items
  3. assign a width to the wrapper element using the following formula: number of columns * width of each item (in our case that's 200px)

Doing that we force the overflow in the parent element, and the scrollbar will have a normal behavior, showing every element.

The full code is available in jsfiddle.

Check the following example:

function onResize() {

  var wrapper = document.querySelector('#wrapper');

  var items = wrapper.querySelectorAll('li');

  var columns = Math.ceil(items.length / 3);

  var width = 200 * columns;

  wrapper.style.width = width > window.innerWidth ? width + 'px' : '100%';

}

onResize();

window.addEventListener('resize', onResize);
html,
body {
  height: 100%;
  text-align: center;
}
*,
*:before,
*:after {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}
#wrapper {
  height: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background-color: aqua;
}
ul {
  height: 75%;
  list-style: none;
  display: flex;
  align-content: center;
  flex-direction: column;
  flex-wrap: wrap;
}
li {
  flex-basis: calc(100% / 3 - 2px);
  color: firebrick;
  border: 1px solid firebrick;
  background-color: greenYellow;
  width: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
}
<div id="wrapper">
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
  </ul>
</div>
Romulo
  • 4,896
  • 2
  • 19
  • 28
  • Can you please add a JSFiddle? – Jessica Nov 08 '15 at 22:37
  • @Jessica there you go – Romulo Nov 08 '15 at 22:39
  • Thank you very much! I'm still trying to understand it. Just curious, is it important (or why did you) round it up (`Math.ceil`)? – Jessica Nov 08 '15 at 22:54
  • @Jessica lets imagine you have 10 items. if i wasn't rounding up the number of columns would be 3 instead of 4, and the width calculations would be wrong and the wrapper would be smaller than needed by 1 item. – Romulo Nov 08 '15 at 22:58
  • In JSLint, I'm getting the following error: `Missing radix parameter. var column...` – Jessica Nov 08 '15 at 23:16
  • @Jessica check [Missing radix parameter](https://jslinterrors.com/missing-radix-parameter). you can fix that by setting the radix in the `parseInt` call. In fact we can simple remove it since we are getting a number from the Math.ceil. – Romulo Nov 08 '15 at 23:20
  • Good point! Also, I'm trying to do this in JQuery, When I try doing: `var wrapper = $('#wrapper')` it doesn't work. Do you know why? – Jessica Nov 08 '15 at 23:28
  • Can't even guess until you post the error / screenshot. Should have worked. Maybe you are calling it before the element exists? – Romulo Nov 08 '15 at 23:31
  • @Jessica there you go, fixed for you [here](http://jsfiddle.net/dsf9a0co/1/). The jquery selector was working fine, however you were using the querySelectorAll in the jquery object, and that is not a jquery function. If you are going to use jquery change all the selectors. – Romulo Nov 08 '15 at 23:35
  • In the last JSFiddle you created, I think you may have forgotten "`width >`" Here's an updated version of the [JSFiddle with the fix](http://jsfiddle.net/dsf9a0co/4/). – Jessica Nov 09 '15 at 07:06
  • While Romulo's answer solves your immediate problem, it is not waterproof. Add `ul:before` and `ul:after` with some content to your CSS and you will see that the calculations fail. IMHO I think you should reopen the question as the problem lies in the fact that your code was correct al along, but you stumbled over a FLEXBOX bug regarding your `ul {align-content: center;}`. One would expect the scrollbar to be twice as wide in this case and the scroll-thumb centered inside the (overflowing) parent. Neither FF, CH nor IE show this result. IE however does not cut the leftsided blocks. PLS reopen. – Rene van der Lende Nov 09 '15 at 08:14
  • ....... the question as I think this problem needs more diving into and a waterproof solution properly working around the problem you presented. Tested in FF DE 44+ CH 46+ and IE11+. – Rene van der Lende Nov 09 '15 at 08:15
  • @RenevanderLende While i agree that Jessica probably found a bug in the flexbox model, the original question don't mention the use of pseudo-elements in the wrapper like `:after` and `:before`. Those worries are beyond the scope of the original question, and in my opinion should be addressed to the developers of Gecko and Webkit. – Romulo Nov 09 '15 at 17:49
  • Are you saying it was tested on the those browsers, and worked, or it didn't work? – Jessica Nov 09 '15 at 18:08
  • @Romulo, I got you, but the ::pseudo was just as an example to prove that your solution works in this particular jsFiddle, while assuming that Jessica's final html would be more sophisticated. I was just looking ahead, but stricktly speaking you are absolutely right, no argument from me. – Rene van der Lende Nov 09 '15 at 19:33
  • @Romulo: in addition: I further assumed, that if LI's are added/removed at runtime while the user scrolls, then resizing would not do the trick. I'm not pickering at all, just doing a lot of assuming (and trying to find the best, failssafe solution). – Rene van der Lende Nov 09 '15 at 19:43
  • @Jessica FF and CH show the same result as your initial problem, while IE doesn't, but all three browser don't update the scrollbar's thumb position. Be warned though, Romulo's solution does work for your final jsFiddle, but should you add/remove LI's on the fly, it will no longer. – Rene van der Lende Nov 09 '15 at 19:47
0

remove align-content and use margin-left:20%; margin-right:auto;

  • That doesn't center it. I'm not sure you completely understand the issue here. What I want is the right and left side of the `ul` to always have the same amount of distance from the browser window. – Jessica Nov 08 '15 at 05:53
  • means, you don't want any kind of overflow? – Shudhansh Shekhar Nov 08 '15 at 05:56
  • I want the overflow. The problem with my JSFiddle, was that the left side got completely cut off. – Jessica Nov 08 '15 at 06:01
  • The whole problem is the matter of width of li you provided. Do you want to use width in %? – Shudhansh Shekhar Nov 08 '15 at 06:10
  • Because px is the absolute sizing. It will not care of your screen width. – Shudhansh Shekhar Nov 08 '15 at 06:15
  • Actually, try using big percentages, like 50%. The same problem is still there. – Jessica Nov 08 '15 at 06:15
  • Jessica, you will try to move across screen size, when you will use 50% as li width. My suggestion is that, you better use less content in a row with flex-basis value. And provide a proper width in percentage. Everything would be fine. – Shudhansh Shekhar Nov 08 '15 at 06:17
  • 2
    My point was, that it doesn't fix the problem, it just goes around the problem. (Well, it doesn't completely go around it.) – Jessica Nov 08 '15 at 06:19
0

There is the safe keyword to center and keep on-screen. It is only supported on Firefox currently:

ul {
  align-content: safe center;
}
silverwind
  • 3,296
  • 29
  • 31
0

What I ended up doing was to give the parent element a set min-width that covers all the contents.

Akaisteph7
  • 5,034
  • 2
  • 20
  • 43