2

I have to float multiple elements around the midpoint of their element. This is currently solved in css by using display: table / display: table-cell.

The problem with this solution is that each group of blocks must wrapped in an extra element, what makes it hard to layout this responsive. Moreover, the visible order is not correct.

I would like to use javascript, to align the elements by using position: absolute, but I have just no clue how to calculate the offsets. Another option might be to create each group dynamically (depending on the window width / height), and to use the actual css (below) to align the elements.

html, body {
  height: 100%;
}

.blocks {
  display: table;
  margin-left: auto;
  margin-right: auto;
  max-width: 40em;
  width: 100%;
  height: 100%;
}

.group {
  display: table-cell;
  vertical-align: middle;
}

.block {
  background-color: rgb(50, 50, 50);
  color: rgb(255, 255, 255);
  height: 8em;
  line-height: 8em;
  margin: 1em;
  text-align: center;
  width: 8em;
}

/* Debug
------------------------------------------------ */
 .blocks {
  counter-reset: tile;
}
.block:before {
  counter-increment: tile;
  content: counter(tile);
}
<!-- small screens -->
<div class="blocks">
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
  </div>
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
  </div>
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
  </div>
</div>

<hr>

<!-- large screens -->
<div class="blocks">
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
  </div>
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
  </div>
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
  </div>
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
  </div>
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
  </div>
</div>

<!-- huge screens -->
<div class="blocks">
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
  </div>
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
  </div>
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
  </div>
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
  </div>
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
  </div>
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
  </div>
  <div class="group">
    <div class="block"></div>
    <div class="block"></div>
  </div>
</div>

...for completeness, position:absolute approach:

.blocks {
  margin-left: auto;
  margin-right: auto;
  max-width: 40em;
  position: relative;
}
.block {
  background-color: rgb(0, 225, 225);
  border: 1px solid white;
  box-sizing:border-box;
  height: 8em;
  line-height: 8em;
  position: absolute;
  text-align: center;
  width: 8em;
}
.block:nth-of-type(1) {
  top: 8em;
  left: 0;
}
.block:nth-of-type(2) {
  top: 4em;
  left: 8em;
}
.block:nth-of-type(3) {
  top: 0;
  left: 16em;
}
.block:nth-of-type(4) {
  top: 4em;
  left: 24em;
}
.block:nth-of-type(5) {
  top: 8em;
  left: 32em;
}
.block:nth-of-type(6) {
  top: 16em;
  left: 0;
}
.block:nth-of-type(7) {
  top: 12em;
  left: 8em;
}
.block:nth-of-type(8) {
  top: 8em;
  left: 16em;
}
.block:nth-of-type(9) {
  top: 12em;
  left: 24em;
}
.block:nth-of-type(10) {
  top: 16em;
  left: 32em;
}
.block:nth-of-type(11) {
  top: 20em;
  left: 8em;
}
.block:nth-of-type(12) {
  top: 16em;
  left: 16em;
}
.block:nth-of-type(13) {
  top: 20em;
  left: 24em;
}
.block:nth-of-type(14) {
  top: 24em;
  left: 16em;
}
/* Debug
------------------------------------------------ */
 .blocks {
  counter-reset: tile;
}
.block:before {
  counter-increment: tile;
  content: counter(tile);
}
<div class="blocks">
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
</div>

Ideally the layout would respect both, the window width and height. So that the blocks always fit in the current screen size. Could be done with something like this:

var cols = Math.floor(innerWidth / children[0].clientWidth)
var rows = Math.round(innerHeight / children[0].clientHeight)

I have searched now for a while, but have not found any existing solutions. The closest are:

By the way, I'm inspired by https://typekit.com/ ;)

Community
  • 1
  • 1
yckart
  • 32,460
  • 9
  • 122
  • 129

2 Answers2

1

Got it... A percentage based solution, that creates an evenly spaced grid.

It feels a bit hacky, but should work:

function alignAround(elements, columns, gutter, weight) {

  var width = ((2 / 3) * 100) - (gutter / 2)
  var push = 100 - width

  var gutterCount = columns - 1
  var repeatAt = (columns * 2) - 1

  var firstShort = 1
  var lastShort = gutterCount
  var lastLong = repeatAt

  if (weight === 'odd') {
    firstShort = columns + 1
    lastShort = repeatAt
    lastLong = columns
  }

  if (columns > 1) {
    width = ((100 - (gutter * gutterCount)) / columns / 100) * 100
    push = (width + gutter) / 2
  }

  return Array.prototype.forEach.call(elements, function(element, index) {

    var styles = {
      width: width,
      height: width
    }

    if (columns > 1) {
      var i = index + 1

      styles.marginRight = gutter

      if ((i - firstShort) % repeatAt === 0) {
        styles.marginLeft = push
      }

      if ((i - lastShort) % repeatAt === 0) {
        styles.marginRight = push
      }

      if ((i - lastLong) % repeatAt === 0) {
        styles.marginRight = 0
      }
    } else if (columns === 1) {

      if (index & 1) {
        styles[weight === 'odd' ? 'margin-left' : 'margin-right'] = push
      } else {
        styles[weight === 'odd' ? 'margin-right' : 'margin-left'] = push
      }

    }

    for (var key in styles) {
      element.style[key] = styles[key] + '%'
    }
  })
}

alignAround(document.querySelectorAll('.block'), 4, 2)
html, body, .blocks {
  height: 100%;
}

.block {
  background: #e44;
  float: left;
}

/* Debug
------------------------------------------------ */
.blocks {
  counter-reset: tile;
}
.block:before {
  counter-increment: tile;
  content: counter(tile);
}
<div class="blocks">
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
</div>

Sorry, code not commented...

yckart
  • 32,460
  • 9
  • 122
  • 129
0

How about a flexbox grid? You can reorder the items in various widths via the order property of flexbox. See my quick example below of the grid.

.list {
  list-style: none;
  display: flex;
  margin: 0;
  padding: 0;
  flex-flow: column wrap;
  align-items: center;  
  align-content: center;
  justify-content: center;
  height: 500px;
  box-sizing: border-box;
}

.box-wrapper {
  background: lightblue;
  width: 100px;
  height: 100%;
  border: 1px solid blue;
  box-sizing: border-box;
  display: flex;
  flex-flow: column wrap;
  align-items: center;  
  align-content: center;
  justify-content: center;
}

.box {
  background: red;
  border: 1px solid darkred;
  width: 100px;
  height: 100px;
  box-sizing: border-box;
}
<ul class="list">

  <li class="box-wrapper">
    <div class="box"></div>
    <div class="box"></div>
  </li>
  <li class="box-wrapper">
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
  </li>
  <li class="box-wrapper">
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
  </li>
  <li class="box-wrapper">
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
  </li>
  <li class="box-wrapper">
    <div class="box"></div>
    <div class="box"></div>
  </li>
</ul>
Roy
  • 7,811
  • 4
  • 24
  • 47
  • Sure, `flexbox` could replace `display:table` and would solve the ordering problem. However, as the layout must be responsive, the elements have still to be moved in the dom. Or do I miss something? How would you create these 3 layouts with the same markup? https://jsfiddle.net/yckart/3h0gkq42/ – yckart Apr 04 '16 at 12:27