7

I want to create a page where my elements are displayed in a Grid. I want to align the items by line. I want to achieve visually the following result, but I don't know how: https://codepen.io/shirkit/pen/KvZKOE/

And I currently have this:

var back = ["red","blue","gray","black","green","cyan","brown","magenta"];
var i = 0;

$('#container .card').children().each(function() {
 $(this).css('background-color',back[i++]);
 if (i == back.length) i = 0;
});
#container {
 display: flex;
 flex-direction: row;
}

.card {
 max-width: 200px;
  width: 100%;
}

.card .image {
 height: 150px;
}

.card .text {
 height: 100px;
}

.card .list {
 height: 50px;
}

#z1 .image {
  height: 175px;
}

#z2 .text {
  height: 120px;
}

#z3 .list {
  height: 60px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
 <div id="z1" class="card">
  <div class="image"></div>
  <div class="text"></div>
  <div class="list"></div>
 </div>
 <div id="z2" class="card">
  <div class="image"></div>
  <div class="text"></div>
  <div class="list"></div>
 </div>
 <div id="z3" class="card">
  <div class="image"></div>
  <div class="text"></div>
  <div class="list"></div>
 </div>
</div>

Clarification: the heights on the ID selectors #z1 .image, #z2.text #z3 .list are there just for simulating the height that the components would have. Therefore, it's NOT an option to change those values. Also note that height values of those elements/containers are not known in advance, so setting margin is also NOT an option.

The current code shown is not how it's currently, it's just for show how visually currently looks, but it should follow that structure, a container for everything, a card for the element and the items inside the card.

Now I know how to do this if I don't put the items in the .card element, and then using the CSS display: grid in #container. I would have the elements .image/.text/.list as direct children from #container, but this is the last resort, I wanted an alternative.

I'm currently looking for a solution that allows for me to keep the .card elements, and have possible fallbacks.

Edit: The proposed answers that were marked as duplicates does not solves the issue I presented here, or I haven't found any others related.

SHiRKiT
  • 1,024
  • 3
  • 11
  • 30

4 Answers4

3

With your current HTML structure, I'm not familiar with any pure CSS ways of achieving your end result. The key issue is that the individual elements in each adjacent column have no way of relating themselves to their surroundings. If your cells were horizontal, we might be able to use CSS like table-row - because "cells are descendants of rows, never of columns." (W3)

With that said, you didn't mention you were looking for a pure CSS solution, and as you already are using jQuery, I thought that would be a fine solution for keeping your HTML structure while achieving your desired layout.

In the code below I'm iterating through each set of components (image, text, list) to determine which card has the tallest one for each, then spacing out the other cards using margin-bottom on each of those shorter components to make them equal.

var back = ["red", "blue", "gray", "black", "green", "cyan", "brown", "magenta"];
var i = 0;

$('#container .card').children().each(function() {
  $(this).css('background-color', back[i++]);
  if (i == back.length) i = 0;
});

var components = ['image', 'text', 'list'];
$.each(components, function(i, j) {
  var $elements = $('.card .' + j);
  var heights = [];
  var tallest = 0;

  $elements.each(function(i) {
    var height = $(this).height();
    heights[i] = height;
    if (height > tallest) {
      tallest = height;
    }
  });

  $.each(heights, function(i, j) {
    var diff = tallest - j;
    if (diff) {
      $elements.eq(i).css('margin-bottom', diff);
    }
  });
});
#container {
  display: flex;
}

.card {
  max-width: 200px;
  width: 100%;
}

.card .image {
  height: 150px;
}

.card .text {
  height: 100px;
}

.card .list {
  height: 50px;
}

#z1 .image {
  height: 175px;
}

#z2 .text {
  height: 120px;
}

#z3 .list {
  height: 60px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
  <div id="z1" class="card">
    <div class="image"></div>
    <div class="text"></div>
    <div class="list"></div>
  </div>
  <div id="z2" class="card">
    <div class="image"></div>
    <div class="text"></div>
    <div class="list"></div>
  </div>
  <div id="z3" class="card">
    <div class="image"></div>
    <div class="text"></div>
    <div class="list"></div>
  </div>
</div>
Jon Uleis
  • 17,693
  • 2
  • 33
  • 42
  • 1
    Although I wanted a pure CSS way, I'm not familiar with a way to do it like that, so a little bit of jQuery solves. I'll wait to see if anyone knows a pure CSS way to do it before rewarding, since this indeed does visually achieves what I wanted. – SHiRKiT Aug 21 '17 at 17:46
  • 1
    I ended up doing a single column for mobile and used this for computers. Since it get's the job done without much overhead for PCs, I'll use it. Thanks for the help! – SHiRKiT Aug 28 '17 at 00:17
0
#container {
 display: flex;
 flex-direction: row;
}

.card {
 max-width: 200px;
  width: 100%;
}

#z2  .image {
 height: 150px;
 max-height: 175px;
 margin-bottom:25px;
}
#z3  .image {
 height: 150px;
 max-height: 175px;
 margin-bottom:25px;
}
#z1 .text {
 height: 100px;
 margin-bottom:20px;
}
#z3 .text {
 height: 100px;
 margin-bottom:20px;
}
.card .list {
 height: 50px;
}

#z1 .image {
  height: 175px;
}
#z2 .image{

}

#z2 .text {
  height: 120px;
}

#z3 .list {
  height: 60px;
}

This is the css for your desired output. But u should use the jquery its recomended You wanted pure css this is it.

swogat
  • 1
  • 4
  • 1
    I added a clarification to the question, since the height of the elements are not known before rendering, so setting margin to absolute values is not a solution. – SHiRKiT Aug 22 '17 at 13:00
  • oh ok, i think i can do it :-p – swogat Aug 23 '17 at 02:33
0

Instead of below code

<style type="text/css">
#z1 .image {
  height: 175px; 
}
#z2 .text {
  height: 120px;
}
#z3 .list {
  height: 60px;
}
</style>

Use this code

<style type="text/css">
#z1 .image {
  height: auto; 
}
#z2 .text {
  height: auto;
}
#z3 .list {
  height: auto;
}
</style>
Manoj Balakonda
  • 128
  • 1
  • 11
  • 1
    I added a clarification, but the height of the id selectors are there for simulating the actual height that the image, text and list would have. Having them set to auto and then having elements inside them that are bigger breaks the behaviour. – SHiRKiT Aug 22 '17 at 12:57
0

Use a masonry layout for this, e.g. https://masonry.desandro.com/. It will help achieve the layout below with a bit of JS. Using pure CSS may not achieve a fully-cross browser solution, especially if you try using flexbox

Masonry layout sample

William
  • 740
  • 2
  • 11
  • 18
  • Sorry but a Masonary layout does not help me achieve the desired effect. I wanted to align the images, text and the list, and this actually makes the problem worse. – SHiRKiT Aug 23 '17 at 21:01
  • Use flexbox then. Check this out: https://codepen.io/dudleystorey/pen/eAqzk – William Aug 24 '17 at 10:08
  • @William That doesn't help either unless you can show how to achieve the effect OP wants using the HTML markup from the original question. – Jon Uleis Aug 24 '17 at 13:56
  • 1
    I'm sorry but I'm quite familiar with Masonary Style and it doesn't do the job. Thanks for the suggestion though. – SHiRKiT Aug 28 '17 at 00:14