Как заставить изображения оставаться в строках контейнера сетки css?

В приведенном ниже коде показано предполагаемое поведение при изменении размера окна в Chrome 60 и Firefox 55 (но не в iOS Safari 10.3; это, скорее всего, еще один вопрос, почему он работает неправильно в Safari).

html, body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background-color: lightgrey;
}

.container {
  box-sizing: border-box;
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: repeat(3, calc((60vh - 12px)/3));
  /*grid-template-rows: 1fr 1fr 1fr;*/
  /*grid-template-rows: auto auto auto;*/
  height: 60vh;
  border: 3px solid yellow;
  padding: 3px;
  /*grid-gap: 20px;*/ /* <-- would also mess things up */
}

.tile {
}

img {
  box-sizing: border-box;
  display: block;
  object-fit: contain;
  width: 100%;
  height: 100%;
  margin: 0;
  border: 0;
  padding: 3px;
}
 <!-- The image is 200 x 100 px: a green and a blue square next to each other. -->
 <div class="container">
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
 </div>

Важно, чтобы соотношение сторон изображений (2:1)

фиктивное изображение

сохраняется. Я ожидал либо:

grid-template-rows: 1fr 1fr 1fr;

or:

grid-template-rows: auto auto auto;

изображения помещаются в строки сетки, но ни одно из них не подходит. С:

grid-template-rows: repeat(3, calc((60vh - 12px)/3));

Я получаю желаемое поведение. Как мне избежать самостоятельной математики? Другими словами, что мне нужно сделать, чтобы grid-template-rows: 1fr 1fr 1fr; (или что-то подобное) работало?

Вычислить высоту элемента-контейнера в CSS на реальной странице уже сложно. Цель состоит в том, чтобы решить эту проблему с помощью макета сетки CSS; без JavaScript и без фоновых изображений.


Обновление: изначально я исключил взлом фонового изображения по двум причинам.

  1. Я думал (из-за некоторых недоразумений), что URL-адрес фонового изображения должен быть в файле CSS, но это не так: я могу использовать встроенные стили и иметь его в HTML.

  2. Это казалось хакерским. Увидев, насколько сложным и запутанным становится использование вложенных flex-контейнеров внутри grid-контейнера только для того, чтобы заставить его работать в Safari, я просто прибегнул к хаку с фоновым изображением, так как он значительно чище и работает во всех проверенные браузеры (Chrome, Firefox, Safari).

В конце концов, это не принятый ответ, который помог решить мою проблему.


person Ali    schedule 19.09.2017    source источник
comment
Ответ на ошибку CSS Grid Row Height Safari, кажется, указывает, почему она ведет себя по-разному в Safari: Проблема в том, что Safari не распознает height: 100% в элементах img.   -  person Ali    schedule 19.09.2017
comment
Какой высоты вы хотите, чтобы эти строки были, и как вы ожидаете, что сетка узнает об этом? Вы только установили контейнер сетки на высоту 60vh, но не установили высоту для строк сетки.   -  person TylerH    schedule 19.09.2017
comment
@TylerH Хорошие вопросы. Я бы хотел, чтобы сетка растягивалась до нижней части страницы (заполняла пустое место на странице), хотя я не знаю, как это сделать. :-( В настоящее время я вычитаю высоту верхнего и нижнего колонтитула из 100vh, использую calc() и устанавливаю это как высоту контейнера. Извините, я абсолютный новичок в этой области. Что касается вашего другого вопроса: строки должны быть одинаковой высоты.   -  person Ali    schedule 19.09.2017
comment
Итак, у вас есть верхний и нижний колонтитулы, которые вместе составляют 40vh + 12px, более или менее?   -  person TylerH    schedule 19.09.2017
comment
@TylerH К сожалению, формула сложнее. :-( Моя жизнь была бы намного проще, если бы я мог поместить верхний и нижний колонтитулы в контейнер сетки, а затем установить высоту сетки на 100vh. Или есть способ сделать контейнер div растягивается до нижней части страницы (или до нижнего колонтитула)? Похоже, мне нужно указать высоту какого-то элемента...   -  person Ali    schedule 19.09.2017


Ответы (3)


У вас есть изображения, установленные на height: 100%. Но 100% чего? 100% контейнер? 100% области просмотра? 100% ряд? Если да, то какова высота строки?

Chrome и Firefox делают обоснованное предположение о ваших намерениях. Они внедрили алгоритмы, разработанные для того, чтобы выйти за рамки рекомендаций по спецификации, чтобы улучшить взаимодействие с пользователем. Эти изменения называются "вмешательствами".

Сафари этого не делает. Safari строго придерживается языка спецификации, в котором говорится, что высота элемента в процентах должна иметь определенную высоту родительского элемента, в противном случае она игнорируется.

Эти различия в браузерах объясняются более подробно здесь:

Затем вы должны учитывать, что элементы сетки по умолчанию не могут быть меньше их содержимого. Если для ваших строк задано значение 1fr, но изображения выше отведенного места, строки должны расширяться. Вы можете переопределить это поведение с помощью min-height: 0 / min-width: 0 или overflow с любым значением, кроме visible.

Более подробно это поведение объясняется здесь:

Тем не менее, после того, как вы примете во внимание приведенное выше руководство, вы, вероятно, сможете заставить свой макет работать в Safari с комбинацией свойств сетки и гибкости:

* {
  box-sizing: border-box;
}

body {
  display: flex;
  flex-direction: column;
  height: 100vh;
  margin: 0;
  background-color: lightgrey;
}

header,
footer {
  flex: 0 0 100px;
  background-color: tomato;
  display: flex;
  align-items: center;
  justify-content: center;
}

.container {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: auto;
  padding: 3px;
}

.tile {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 0;
}

img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  padding: 3px;
}
<header>HEADER</header>
<!-- The image is 200 x 100 px: a green and a blue square next to each other. -->
<div class="container">
  <div class="tile">
    <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
  </div>
  <div class="tile">
    <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
  </div>
  <div class="tile">
    <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
  </div>
  <div class="tile">
    <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
  </div>
  <div class="tile">
    <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
  </div>
  <div class="tile">
    <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
  </div>
</div>
<footer>FOOTER</footer>

jsFiddle

person Michael Benjamin    schedule 19.09.2017
comment
Я кратко искал что-то вроде stackoverflow.com/questions/43311943/ - я чувствую, что это может быть дубликат этого. - person TylerH; 19.09.2017
comment
@TylerH, в коде этой ОП есть несколько проблем, требующих внимания. Ссылка, которую вы упомянули, охватывает проблему минимального размера. Если бы это было единственной проблемой, я бы согласился, это обман. Однако есть также проблемы с процентной высотой и вариантами рендеринга в браузере. - person Michael Benjamin; 19.09.2017
comment
@Michael_B Хммм. На iPhone в Safari 10.3 это совершенно испорчено. :-( - person Ali; 19.09.2017
comment
Как я уже писал в своем ответе, вам придется это решить. Поскольку у меня нет всех особенностей вашего макета, я просто дал рекомендации. Я бы посоветовал вам просмотреть ссылки в моем ответе. В частности, этот: stackoverflow.com/q/44770074/3597276 - person Michael Benjamin; 19.09.2017
comment
@Michael_B Достаточно честно. Я попытаюсь решить это самостоятельно, и если мне это не удастся, я опубликую еще один вопрос с более сложным кодом, показывающим больше фактического макета. - person Ali; 20.09.2017

Вы можете использовать grid-template-rows: 1fr 1fr 1fr и, что более важно, вы должны сбросить значения min-width и min-height элементов сетки, которые по умолчанию равны auto (как и содержимое).

Чтобы обеспечить более разумный минимальный размер по умолчанию для элементов сетки, эта спецификация определяет, что автоматическое значение min-width/min-height также применяет автоматический минимальный размер по указанной оси к элементам сетки, переполнение которых видимо и которые охватывают хотя бы один дорожка, для которой функция определения размера минимальной дорожки установлена ​​автоматически. (Эффект аналогичен автоматическому минимальному размеру, налагаемому на гибкие элементы.)

Источник: W3C

Это похоже на правило auto flex item для flexboxes. См. демонстрацию ниже, где я сбросил их на ноль:

html, body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background-color: lightgrey;
}

.container {
  box-sizing: border-box;
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
  height: 60vh;
  border: 3px solid yellow;
  padding: 3px;
  /*grid-gap: 20px;*/ /* <-- would also mess things up */
}

.tile {
  min-width: 0;
  min-height: 0;
}

img {
  box-sizing: border-box;
  display: block;
  object-fit: contain;
  width: 100%;
  height: 100%;
  margin: 0;
  border: 0;
  padding: 3px;
}
<!-- The image is 200 x 100 px: a green and a blue square next to each other. -->
 <div class="container">
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
 </div>

person kukkuz    schedule 19.09.2017

Я не знаю, насколько плотно вы хотите разместить изображения, но вы можете использовать minmax(). minmax() позволяет установить минимальное и максимальное значение размера строки сетки. Установка auto для минимума и 33% для максимума позволит им стать настолько маленькими, насколько это необходимо для содержимого, и до 33% высоты контейнера сетки, но не больше. Это позволит сохранить все ваши элементы сетки вместе на максимальной высоте 99% от 60vh, которые занимает контейнер сетки.

Это не совсем тот автоматический способ, который вы надеялись получить... вы все равно объявляете размер, даже если он относительный. Это позволяет избежать неуклюжего вида calc((60vh - 12px) / 3), хотя в использовании этого метода нет ничего плохого, если только в вашем сообщении нет других ограничений.

Однако ответ kukkuz и сброс min-height - лучшее решение, и это то, чего мне не хватало.

html, body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background-color: lightgrey;
}
.container {
  box-sizing: border-box;
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: repeat(3, minmax(auto, 33%));
  height: 60vh;
  border: 3px solid yellow;
  padding: 3px;
}
.tile {
    display: grid;
}
img {
  box-sizing: border-box;
  display: block;
  object-fit: contain;
  width: 100%;
  height: 100%;
  margin: 0;
  border: 0;
  padding: 3px;
}
 <!-- The image is 200 x 100 px: a green and a blue square next to each other. -->
 <div class="container">
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
 </div>

person TylerH    schedule 19.09.2017
comment
К сожалению, это не работает в Chrome 60. :-( Изображения переполняются. - person Ali; 19.09.2017
comment
@Ali Увы, я стал самодовольным, используя Firefox, и забыл, что Chrome любит ломать много вещей. Спасибо, что дали мне знать; Я посмотрю и посмотрю, что я могу сделать, чтобы решить проблему с Chrome. - person TylerH; 19.09.2017
comment
@Ali Похоже, Chrome не любит распространять свойства grid-children на дочерние элементы display: grid элементов. Я обновил фрагмент, и теперь он должен работать в Chrome. У меня нет Safari для тестирования. - person TylerH; 19.09.2017
comment
Спасибо, я ценю ваши усилия, но мне все еще не нравится предложенное решение. Не могли бы вы немного расширить свой ответ, пожалуйста? Почему и как ваш ответ решает проблему? Что было не так с моими попытками? Такое объяснение будет полезно и будущим посетителям Google. (Предполагая, конечно, что они хотят учиться, а не просто слепо копировать и вставлять код, не понимая его сначала...) - person Ali; 19.09.2017
comment
@Ali Я добавил немного больше информации о minmax, но ответ kukkuz - более чистое решение. Я еще не читал ответ Michael_B, но, вероятно, он тоже чище. - person TylerH; 19.09.2017
comment
Хорошо, я посмотрел ваш обновленный ответ. Проблема в том, что на реальной веб-странице .tile является flex-контейнером... :-( В любом случае, ошибка в Chrome не по вашей вине. Я проголосовал за ваш ответ, чтобы поблагодарить вас за ваши усилия! - person Ali; 20.09.2017