skip to Main Content

How can I create the following behaviour using CSS or Javascript:

  • I want to have container (probably flexgrid) that have children
  • The container children should be the same size, distributed evenly and cover all the container space (as much as possible)
  • The children must keep ratio of 3:4
  • The container width, height and the amount of children are dynamic
  • The children size should be dynamic based on the height width and the children amount

Partial solution: https://codepen.io/lerman01/pen/poGNmry

the problem with my solution is that each time you change the container width, height or amount of children you need to recalculate the value of the container’s grid-template-columns value

For example

container 550×260 px with 2 children:
enter image description here

container 550×260 px with 20 children:
enter image description here

2

Answers


  1. To maintain aspect ratio, you can use aspect-ratio property in css, we can ignore the height and width, or can use any either height or width only , incase we have used both height and width then the aspect-ratio will not work properly. Can read more on MDN docs aspect-ratio , also all the major browser supports aspect-ratio property refer caniuse.com/aspect-ratio

    .parent-container {
      border: 1px solid red;
      display: flex;
      align-items: center;
      flex-wrap: wrap;
    }
    
    .parent-container div {
      border: 1px solid green;
      aspect-ratio: 3 / 4;
      background-color: silver;
      margin: 5px;
    }
    <div class="parent-container">
          <div>1 1 1 1 1</div>
          <div>2 2 2 2 2 2 2 2 2 2 2</div>
          <div>3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3</div>
          <div>4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4</div>
          <div>5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 </div>
        </div>
    Login or Signup to reply.
  2. A possible approach is to start by setting the height of child div to its maximum available value (=parent_height – vertical_gap – 2*child_border) and then calculate the child div width by the aspect-ratio condition.

    Then we can calculate total-width of divs and check if total-width and total-height are in parent-div bounds. Loop this check by decrementing div height until it fits.

    window.addEventListener('load', calc());
    
    var rows, cols, child_width, child_height, child_gap;
    
    function total_width() {
        return cols * (child_width + 2 + child_gap);
    }
    
    function total_height() {
        return rows * (child_height + 2 + child_gap);
    }
    
    function calc() {
        const container = document.querySelector('.container');
        const container_width = document.getElementById('ct_width').valueAsNumber;
        const container_height = document.getElementById('ct_height').valueAsNumber;
        const number_of_childs = document.getElementById('ch_count').valueAsNumber;
        child_gap = document.getElementById('ch_gap').valueAsNumber;
    
        container.innerHTML = '';
        container.style.width = container_width + 'px';
        container.style.height = container_height + 'px';
        container.style.gap = child_gap + 'px';
    
        rows = 1;
        cols = number_of_childs;
        child_height = Math.floor((container_height - rows * (child_gap + 2)) / rows);
        child_width = Math.floor(child_height * 3 / 4);
        while (total_height() > container_height || total_width() > container_width) {
            child_height--;
            child_width = Math.floor(child_height * 3 / 4);
            rows = Math.floor(container_height / (child_height + 2 + child_gap));
            cols = Math.ceil(number_of_childs / rows);
        }
    
        document.getElementById("ch_width").value = child_width;
        document.getElementById("ch_height").value = child_height;
        document.getElementById("ch_aspect_ratio").value = ((child_width / child_height) * 4).toFixed(2) + ' To 4';
        document.getElementById("coverage").value = Math.round(number_of_childs * (child_width + 2) * (child_height + 2) * 100 / container_width / container_height) + '%';
    
        for (i = 0; i < number_of_childs; i++) {
            var child = document.createElement('div');
            child.classList.add('child');
            child.style.width = child_width + 'px';
            child.style.height = child_height + 'px';
            child.innerText = i;
            container.appendChild(child);
        }
    }
    #params {
                width: 300px;
                display: grid;
                grid-template-columns: auto 100px;
                grid-gap: 4px;
                margin: 15px;
            }
    
                #params label {
                    grid-column: 1 / 2;
                    text-align: right;
                }
    
                #params input {
                    grid-column: 2 / 2;
                }
    
            .container {
                display: flex;
                align-content: flex-start;
                flex-wrap: wrap;
                border: solid 1px red;
                padding: 0px;
            }
    
            .child {
                border: solid 1px blue;
                background-color: burlywood;
            }
    <div id="params">
        <label for="ct_width">Container Width =</label>
        <input type="number" id="ct_width" value="550" max="1000" min="200" onchange="calc()" />
        <label for="ct_height">Container Height =</label>
        <input type="number" id="ct_height" value="260" max="700" min="100" onchange="calc()" />
        <label for="ch_count">Count of Childs =</label>
        <input type="number" id="ch_count" value="20" max="100" min="1" onchange="calc()" />
        <label for="ch_gap">Child Gap =</label>
        <input type="number" id="ch_gap" value="3" max="10" min="0" onchange="calc()" />
        <label for="ch_width">Child Width =</label>
        <input type="text" id="ch_width" readonly="readonly" />
        <label for="ch_height">Child Height =</label>
        <input type="text" id="ch_height" readonly="readonly" />
        <label for="ch_aspect_ratio">Aspect Ratio =</label>
        <input type="text" id="ch_aspect_ratio" readonly="readonly" />
        <label for="coverage">Total Childs Coverage =</label>
        <input type="text" id="coverage" readonly="readonly" />
    </div>
    <div class="container"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search