Золотая рыбка CSS3 Grid Layout (часть 3)

Благодаря нашим недавним погружениям в волшебный мир этого модуля, мы уже смогли прочувствовать его силу и невероятные возможности, которыми он наделён. Но, несмотря на множество примеров и описаний разделов CSS Grid Layout Module Level 1, есть ещё немало сюрпризов, о которых стоит поговорить. Поэтому, как вы уже догадались, в этой статье мы продолжим прогулку по этой спецификации, ведь золотая рыбка задолжала нам ещё желания;)

Кстати, так совпало, что параллельно с нами Эмиль Бьёрклунд также ведёт свою серию статей, посвящённую спецификации Grid Layout. Поэтому, чтобы лучше прояснить для себя механизм этой раскладки, мы настоятельно рекомендуем ознакомиться со статьями Эмиля. Для этого можно пройти к нему в гости, либо прочитать перевод у нас на сайте.

Пожалуй, начнём…

Повторение колонок и рядов с функцией repeat()

Вы наверняка знаете свойство background-repeat, которое отвечает за повторение фоновой картинки в элементе. Вот и в модуле Grid Layout есть нечто похожее, но только для повторения колонок/рядов.

Представляю вам функцию repeat(), при помощи которой можно создать повторяющийся паттерн для построения колонок/рядов, описанный в компактной форме.

Например, возьмём CSS-код из спецификации:

.grid {
  display: grid;
  
  grid-template-columns: 10px [col-start] 250px [col-end]
               10px [col-start] 250px [col-end]
               10px [col-start] 250px [col-end]
               10px [col-start] 250px [col-end] 10px;
}

Обратите внимание на повторяющиеся названия именованных линий, которые находятся в разных скобках. Как вы поняли, так тоже можно делать. Чуть позже в этой статье мы разберём примеры того, где это может пригодится, а пока вернёмся к repeat().

При помощи repeat() то же самое CSS-объявление выглядит так:

.grid {
  display: grid;
  
  /* то же самое объявление с repeat() */
  grid-template-columns: repeat (4, 10px [col-start] 250px [col-end]) 10px;
}

Намного короче, неправда ли? Но давайте разберёмся. Я не стану приводить скриншоты, поскольку здесь достаточно простого объяснения, как работает эта функция. Первый аргумент в repeat(), это число повторений паттерна. В нашем случае паттерн будет повторяться 4 раза. Далее мы уже описываем сам паттерн: величину колонок, именованные линии и т.д. Всё просто. Очевидно, что эта функция очень полезна при большом количестве ячеек. И также не забывайте, что repeat() легко можно использовать и для построения рядов в свойстве grid-template-rows.

Обращение к полосам

По умолчанию грид-элементы распределяются по сетке в обычном порядке, как они идут в разметке. Но что если нам нужно раскидать блоки по конкретным колонкам/рядам? Grid Layout позаботился и об этом, предоставив пару способов. Рассмотрим каждый из них.

Способ с уникальными названиями грид-линий

Ранее (в этой и предыдущих статьях) мы уже научились создавать сетки разными методами, например, при помощи уникальных названий грид-линий. Поэтому в ниже представленной разметке и стилях вы не увидите ничего эдакого.

<div class="grid unique-line-names">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
</div>
.unique-line-names {
   grid-template-columns:
     10px [col1-start] 100px [col1-end]
     10px [col2-start] 100px [col2-end]
     10px [col3-start] 100px [col3-end]
     10px [col4-start] 100px [col4-end]
     10px;
}

Мы воспользовались свойством grid-template-columns, чтобы создать сетку, состоящую из девяти колонок (четыре широких по 100px в ширину и пять узких по 10px). Нечто подобное мы проделывали здесь, только в нашем случае ширина первой колонки (а значит расстояние между началом первой и второй линией) будет 10px, второй (между [col1-start] и [col1-end]) — 100px, третьей — 10px и т.д.

В нашем грид-контейнере находится шесть грид-элементов. Представим, что нам нужно поместить в первую, третью и четвёртую широкие колонки по два блока, а вторую широкую колонку (и соответственно остальные узкие по 10px) пропустить. Для этого нам нужен такой CSS:

.unique-line-names > div:nth-child(3n + 1) {
   grid-column: col1-start;
}

.unique-line-names > div:nth-child(3n + 2) {
   grid-column: col3-start;
}

.unique-line-names > div:nth-child(3n) {
   grid-column: col4-start;
}

И вот результат:

2015-10-10_1728

Я специально сделал границу, чтобы было видно пустые колонки (ячейки грида). Элементы находятся во второй, в шестой и в восьмой колонках, а остальные колонки пустуют. Как вы, возможно, догадались из CSS-кода, произошло это потому, что с помощью свойства grid-column мы обратились к нужным нам колонкам по уникальными именам их границ ([col1-start], [col3-start], [col4-start]), приказав, например, первому и четвёртому элементам (div:nth-child(3n + 1)) оказаться в колонке, начинающейся от линии col1-start.

Способ с одинаковыми названиями грид-линий

Только что благодаря уникальным именам грид-линий мы легко раскидали элементы по нужным колонкам. Но что делать, если названия грид-линий не уникальны? Но и в этом случае Grid Layout нас не подвёл!

В HTML поменяем только класс у грид-контейнера.

<div class="grid same-line-names">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
</div>

А в CSS уберём цифры из названий линий, сделав их одинаковыми:

.same-line-names {
   grid-template-columns:
     10px [col-start] 250px [col-end]
     10px [col-start] 250px [col-end]
     10px [col-start] 250px [col-end]
     10px [col-start] 250px [col-end]
     10px;
}

Мы заменили все col*-start на просто col-start, проделав тоже самое и с col*-end. И теперь при этом нам нужно получить такой же результат, что и в предыдущем примере. Как это сделать? Да легко. Мы просто обратимся к грид-линиям по номерам:

.same-line-names > div:nth-child(3n + 1) {
   grid-column: col-start 1;
}

.same-line-names > div:nth-child(3n + 2) {
   grid-column: col-start 3;
}

.same-line-names > div:nth-child(3n) {
   grid-column: col-start 4;
}

И вот результат. Знакомый, не правда ли?

2015-10-10_17281

Это напоминает массив, только номера начинаются с цифры 1, а не с цифры 0, как мы привыкли. В свойстве grid-template-columns четыре линии col-start, следовательно к первой можно обратиться col-start 1, ко второй col-start 2, и т.д. Понятно, что, например, col-start 1 будет равнозначна col1-start из предыдущего примера. Поэтому, чтобы заставить первый и четвёртый элементы (div:nth-child(3n + 1)) занять вторую колонку, мы просто присвоили их свойству grid-column значение col-start с номером 1.

Автоматическое размещение

Согласно автоматическому алгоритму размещения Grid Layout, грид-элементы выкладываются по сетке в грид-контейнере друг за другом, заполняя собой свободное пространство. Если, например, грид-элементу не хватает места в первом ряду, то он автоматически переходит на второй ряд. Вся прелесть здесь в том, что Grid Layout позволяет нам взять алгоритм автозразмещения под свой контроль! Это напоминает свойство flex-direction в Flexbox, только круче и в контексте Grid Layout.

Давайте рассмотрим это сразу на примерах, начав с простых и закончив более сложным из спецификации.

Для начала воспользуемся уже знакомыми нам свойствами grid-template-columns и grid-template-rows и создадим грид-сетку 3х3, где колонки будут занимать по 200px, а ряды — по 100px. Элементов при этом будет семь, поскольку так нагляднее для понимания алгоритма.

<div class="grid">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
</div>
.grid {
    display: grid;
    grid-template-columns: 200px 200px 200px;
    grid-template-rows: 100px 100px 100px;
}
.grid div {
    margin: 5px;
    border: 1px solid #ccc;
    background: #CCF;
}

И результат:

grid1

Здесь всё вроде бы ясно, поэтому пора познакомиться с новым свойством grid-auto-flow, которое как раз и отвечает за алгоритм размещения грид-элементов по сетке. У этого свойства есть два значения: row, column, плюс к каждому из них может добавляться ещё одно волшебное значение dense. Последнее (dense) мы обсудим чуть позже, а пока посмотрим, как ведёт себя алгоритм без него. Первое значение rows (значение по умолчанию) приказывает элементам выстраиваться слева направо, идя по рядам, заполняя каждую грид-ячейку. Такое поведение можно наблюдать на рисунке выше.

А сейчас давайте поменяем значение свойства grid-auto-flow с row на column.

.grid {
    display: grid;
    grid-template-columns: 200px 200px 200px;
    grid-template-rows: 100px 100px 100px;

    /*меняет поведение алгоритма с row с на column*/
    grid-auto-flow: column;
}

И в итоге…

grid2

Не трудно догадаться, что произошло. Теперь грид-элементы выстраиваются не слева направо, а сверху вниз, следуя уже не по рядам, а по колонкам.

А теперь перейдём к самому страшному более сложному примеру из спецификации, чтобы увидеть всю прелесть значения dense.

Представим, что у нас есть форма с полями, метками и отдельным блоком (на место расположение которого, кстати, стоит обратить особое внимание, см. блок #department-block) с выпадающим списком внутри, ну и блок с кнопками.

<form>
  <label for="firstname">First name:</label>
  <input type="text" id="firstname" name="firstname" />
  <label for="lastname">Last name:</label>
  <input type="text" id="lastname" name="lastname" />
  <label for="address">Address:</label>
  <input type="text" id="address" name="address" />
  <label for="address2">Address 2:</label>
  <input type="text" id="address2" name="address2" />
  <label for="city">City:</label>
  <input type="text" id="city" name="city" />
  <label for="zip">Zip:</label>
  <input type="text" id="zip" name="zip" />

  <div id="department-block">
    <label for="department">Department:</label>
    <select id="department" name="department" multiple>
      <option value="finance">Finance</option>
      <option value="humanresources">Human Resources</option>
      <option value="marketing">Marketing</option>
    </select>
  </div>

  <div id="buttons">
  <button id="cancel">Cancel</button>
  <button id="back">Back</button>
  <button id="next">Next</button>
  </div>
</form>

Наша задача раскидать метки, поля и отдельный блок по своим колонкам (всего три колонки), а блоку с кнопками отвести отдельный ряд в самом низу.

  form {
    display: grid;
    
    grid-template-columns: [labels] auto [controls] auto [oversized] auto;
    
  }
  form > label {
 
    grid-column: labels;
    grid-row: auto;
  }
  form > input, form > select {
 
    grid-column: controls;
    grid-row: auto;
  }

  #department-block{
  
    grid-column: oversized;
    grid-row: span 3;
  }

  
  #buttons {
    grid-row: auto;

    grid-column: 1 / -1;
    text-align: center;
  }

А результат вышел вот такой:

Я немного упростил пример и переделал картинку из спецификации, чтобы было наглядно понятно, что происходит

grid3-1

Для начала давайте разберёмся, что вообще происходит. В этом примере собрано множество грид-механизмов, с которыми мы уже знакомы. Например, взять вот это объявление:

grid-template-columns: [labels] auto [controls] auto [oversized] auto;

Мы уже проделывали подобный трюк в этой статье (см. раздел «Способ с уникальными названиями грид-линий»), где давали уникальные названия грид-линиям, чтобы потом разбросать нужные элементы по «именованным колонкам». Единственное отличие здесь в том, что расстояние между линиями (колонками) не фиксированное (auto), поэтому ширина колонок определяется шириной элементов.

Дальше у нас идут такие блоки объявлений:

form > label {
 
    grid-column: labels;
    grid-row: auto;
}

  form > input {
 
    grid-column: controls;
    grid-row: auto;
  }

  #department-block{
  
    grid-column: oversized;
    grid-row: span 3;
  }

Свойство grid-column нам также знакомо, поэтому вы уже наверное догадались, что для меток (label) у нас отведена первая колонка, для полей — вторая, а для блока #department-block — третья.

Здесь стоит отметить несколько важных моментов. У свойства grid-row меток и полей стоит значение auto, поэтому ряды будут заполняться по мере необходимости. С этим всё понятно, но о чём нам говорит значение span 3 у свойства grid-row блока #department-block? О том, что блок #department-block будет занимать три ряда (span 3).

А вот в каком ряду будет располагаться #department-block, нам расскажет наше загадочное значение dense.

Без dense алгоритм может «переть» только вперед, не оглядываясь назад. Поскольку метки и поля могут находиться только в своих выделенных колонках, то при их размещении алгоритм вынужден опускаться всё ниже и ниже. Когда он доходит до блока #department-block, алгоритм находится на 6 ряду — столько перед ним было меток и полей — и этот блок продолжает этот же 6 ряд.

А вот теперь мы подошли к самому главному — к dense — третьему значению свойства grid-auto-flow. Давайте посмотрим, что может сотворить эта мощная штука.

  form {
    display: grid;
    
    grid-template-columns: [labels] auto [controls] auto [oversized] auto;
    
    /* волшебное значение dense*/
    grid-auto-flow: dense;

И вот результат:

grid-layout-css3

Как видно на рисунке, блоку #department-block больше не приходится опускаться вниз и ждать своей очереди, чтобы занять свой ряд. Благодаря dense алгоритм теперь учитывает пропущенные строки и размещает #department-block сразу же в первой строке его ряда.

P.S. Это тоже может быть интересно:

2 Комментарии

  1. Дмитрий

    Я вот не пойму, почему вы так много пишите про css grid layout?
    Их же кроме IE последних версий никто не поддерживает. Или ситуация вскоре измениться?

    1. SelenIT

      Ситуация уже изменилась:). IE10+ поддерживает только капитально устаревшую версию. Зато актуальную версию (про которую мы пишем) поддерживает Хром (правда, пока за флагом, зато начиная с 50-й версии — правильно), и Firefox (причем начиная с 45-й версии, релиз которой ожидается в начале марта — без всяких флагов и префиксов!). Так что уже этим летом, скорее всего, гриды будут массово доступны в подавляющем большинстве браузеров. А возможности там такие, что флексбоксы рядом не стояли:)

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.