Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Left aligned and centered grid with flexbox

Tags:

css

flexbox

I'd like to implement a responsive grid-like layout using flexbox (without media queries). There can be variable number of elements in the grid. Each item should have fixed and equal width. Items should be aligned to the left. Whole group should have equal left and right margins.

It should look like that:

Expected result

This is how I tried to achieve it:

.container {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  margin: auto;
}
.item {
  height: 200px;
  width: 200px;
  background-color: purple;
  padding: 10px;
  margin: 10px;
}
<div class="container">
  <div class="item">Flex item 1</div>
  <div class="item">Flex item 2</div>
  <div class="item">Flex item 3</div>
  <div class="item">Flex item 4</div>
  <div class="item">Flex item 5</div>
</div>

It didn't work:

Actual result

I was hoping that setting margin: auto on a container will force it to have width just enough to fit optimal number of items in each row.

I know I can make it easily using framework like Bootstrap or Foundations but I wonder if it's possible to use flexbox as well.

like image 852
Dorota Cieslinska Avatar asked Sep 14 '15 19:09

Dorota Cieslinska


People also ask

How do you left align on Flexbox?

For aligning columns to the left, the align-content property will set to 'flex-start'. For aligning columns to the right, the align-content property will set to 'flex-end'. For aligning columns to the extreme ends, the align-content property will set to 'space-between'.

How do you center items in flex grid?

We make the container a Flexbox by setting display: flex . Next, we set the align-items property to the center value, which aligns each div item in the center.

Can we use both grid and Flexbox together?

You can use them together but not necessarily one the same element. For instance I can place a div in a CSS-Grid parent container (which has display:grid ) but the child div could have display:flex to lay out it's children.


1 Answers

CSS Grid

CSS Grid solution is the only elegant and flexible CSS solution.

.container {
  display: grid;
  grid-template-columns: repeat(auto-fill, 200px);
  grid-auto-rows: 200px;
  grid-gap: 10px;
  justify-content: center;
}

.item {
  background-color: purple;
  padding: 10px;
}
<div class="container">
  <div class="item">Flex item 1</div>
  <div class="item">Flex item 2</div>
  <div class="item">Flex item 3</div>
  <div class="item">Flex item 4</div>
  <div class="item">Flex item 5</div>
</div>

Unfortunately, it doesn't have the best browser support, so if you need outdated browsers like IE you'll have to use Javascript to achieve this. It's due the fact that flexbox container occupies all width even in case when items don't.

Pure Javascript version

So basically we are calculating needed value of margin-left to center our flexbox items. This Javascript should work regardless of initial markup settings.

// get width of element with margins
function getOuterWidth(el) {
  var styles = window.getComputedStyle(el);
  var margins = parseFloat(styles["marginLeft"]) +
    parseFloat(styles["marginRight"]);

  return Math.ceil(el.getBoundingClientRect().width + margins);
}

// get width of element without paddings
function getContentWidth(el) {
  var styles = window.getComputedStyle(el);
  var paddings = parseFloat(styles["paddingLeft"]) +
    parseFloat(styles["paddingRight"]);

  return Math.ceil(el.getBoundingClientRect().width - paddings);
}

// Get top of element
function getTopOfElement(el) {
  return el.getBoundingClientRect().top;
}

var container = document.querySelector(".container");
var initialMarginLeft = parseFloat(window.getComputedStyle(container)["marginLeft"]);
// getting array of items
var items = Array.prototype.slice.call(document.querySelectorAll(".item"));

function centerItems() {
  if (items.length === 0) return 0;

  // set margin-left to initial value to recalculate it
  container.style.marginLeft = initialMarginLeft + "px";

  var topOfFirstItem = getTopOfElement(items[0]);
  var spaceTakenByElementsOnFirstLine = getOuterWidth(items[0]);

  for (var i = 1; i < items.length; i++) {
    // Break in case we are in second line
    if (getTopOfElement(items[i]) > topOfFirstItem)
      break;
    spaceTakenByElementsOnFirstLine += getOuterWidth(items[i]);
  }

  // Set margin-left to center elements
  var marginLeft = initialMarginLeft + (getContentWidth(container) - spaceTakenByElementsOnFirstLine) / 2;

  container.style.marginLeft = marginLeft + "px";
};

window.addEventListener("resize", centerItems);

centerItems();
.container {
  display: flex;
  flex-wrap: wrap;
}

.item {
  height: 200px;
  width: 200px;
  background-color: purple;
  padding: 10px;
  margin: 10px;
}
<div class="container">
  <div class="item">Flex item 1</div>
  <div class="item">Flex item 2</div>
  <div class="item">Flex item 3</div>
  <div class="item">Flex item 4</div>
  <div class="item">Flex item 5</div>
</div>

jQuery version

// get width of element with margins
function getOuterWidth(el) {
  return $(el).outerWidth(true);
}

// get width of element without paddings
function getContentWidth(el) {
  return parseFloat($(el).css("width"));
}

function getTopOfElement(el) {
  return $(el).position().top;
}

var $container = $(".container");
var $items = $(".item");
var initialMarginLeft = parseFloat($container.css("margin-left"));

function centerItems() {
  if ($items.length === 0) return 0;

  // set margin-left to initial value to recalculate it
  $container.css("margin-left", initialMarginLeft + "px");

  var topOfFirstItem = getTopOfElement($items[0]);
  var spaceTakenByElementsOnFirstLine = getOuterWidth($items[0]);

  for (var i = 1; i < $items.length; i++) {
    // Break in case we are in second line
    if (getTopOfElement($items[i]) > topOfFirstItem)
      break;
    spaceTakenByElementsOnFirstLine += getOuterWidth($items[i]);
  }

  // Set margin left to center elements
  var marginLeft = initialMarginLeft + (getContentWidth($container) - spaceTakenByElementsOnFirstLine) / 2;

  $container.css("margin-left", marginLeft + "px");
};

$(window).resize(centerItems);

centerItems();
.container {
  display: flex;
  flex-wrap: wrap;
}

.item {
  height: 200px;
  width: 200px;
  background-color: purple;
  padding: 10px;
  margin: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="container">
  <div class="item">Flex item 1</div>
  <div class="item">Flex item 2</div>
  <div class="item">Flex item 3</div>
  <div class="item">Flex item 4</div>
  <div class="item">Flex item 5</div>
</div>
like image 165
Vadim Ovchinnikov Avatar answered Oct 07 '22 15:10

Vadim Ovchinnikov