Равномерное распределение элементов в блоке (html/css)

У меня есть родительский элемент с фиксированной шириной и высотой. Мне нужно равномерно распределить N дочерних элементов по всей высоте и ширине.

Также нужно учитывать следующие моменты:

  1. Все дочерние элементы должны иметь одинаковую высоту (не фиксированную);
  2. Важно: высота и ширина у дочерних элементов имеет соотношение 4 (width) к 3 (height);
  3. Родительский элемент имеет фиксированную ширину и высоту, то есть не должен иметь скролла даже при сотне дочерних элементов;

Рассмотрим данный пример:

.parent {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
  
  padding: 10px;
  gap: 10px;
  width: 400px;
  height: 150px;
  background-color: gray;
}

.child {
  flex: 1 1 auto;
  
  width: 100px;
  background-color: black;
  border: solid 1px white;
}
<div class='parent'>
  <div class='child'>beer 1</div>
  <div class='child'>beer 2</div>
  <div class='child'>beer N</div>
  <div class='child'>beer N+1</div>
  <div class='child'>beer</div>
</div>

В данном примере все дочерние элементы имеют одинаковую высоту. Нижние элементы имеют разную длину (неприятно, но не критично). Элементы не имеют разрешение 4/3, от которых и должно идти корректное распределение.

Возможно эту задачу можно решить с помощью вычислений в js, но я не придумал хорошей формулы. Также вероятно это возможно с помощью grid, но опять я не смог это сделать.


Ответы (2 шт):

Автор решения: Owlly

Можно попробовать решить эту задачу с помощью CSS Grid и динамических вычислений, чтобы подстраивать количество колонок и строк в зависимости от количества дочерних элементов

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(0, 1fr));
  justify-content: center;
  gap: 10px;
  padding: 10px;
  width: 400px;
  height: 150px;
  background-color: gray;
  overflow: hidden;
}

.child {
  background-color: black;
  border: solid 1px white;
  aspect-ratio: 4 / 3;
  /* Соотношение 4 к 3 */
}
<div class="parent">
  <div class="child">beer 1</div>
  <div class="child">beer 2</div>
  <div class="child">beer N</div>
  <div class="child">beer N+1</div>
  <div class="child">beer</div>
</div>

→ Ссылка
Автор решения: EzioMercer

Могу предложить такой вариант на flex:

  1. Родителю задаём:

    display: flex;
    flex-wrap: wrap;
    justify-content: space-evenly;
    align-content: space-evenly;
    
    • align-items потому что у нас много строк
    • space-evenly т.к. хотим чтобы расстояния были одинаковыми
  2. Дочерним задаём:

    min-width: 4px;
    aspect-ratio: 4 / 3;
    

const parent = document.querySelector('div');
const countRange = document.querySelector('input');
const childrenCountDisplay = document.querySelector('span');

const createChild = () => {
  const child = document.createElement('div');

  child.classList.add('child');

  return child;
}

const updateChildren = () => {
  const count = Number(countRange.value);
  const fragment = document.createDocumentFragment();
  
  childrenCountDisplay.textContent = count;

  for (let i = 0; i < count; ++i) {
    fragment.append(createChild());
  }

  parent.innerHTML = '';
  parent.append(fragment);
}

updateChildren();

countRange.addEventListener('input', () => updateChildren());
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.parent {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-evenly;
  align-content: space-evenly;
  
  padding: 10px;
  gap: 10px;
  width: 400px;
  height: 150px;
  background-color: gray;
}

.child {
  min-width: 4px;
  aspect-ratio: 4 / 3;
  
  background-color: rgba(0, 0, 0, .5);
  outline: solid 1px white;
}
<div class="parent"></div>
<input type="range" min="1" max="200" step="1" value="10" />
<p>Children count: <span>1</span></p>

→ Ссылка