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

В прошлой статье золотая рыбка Grid Layout выполнила для нас множество прекрасных желаний, но тем не менее, это далеко не всё, на что она способна. При помощи волшебства Grid Layout можно творить такие вещи, для которых раньше нам бы понадобилось куча «костылей» и хаков, либо исключительно JavaScript.

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

Важно! Новый синтаксис описания именованных линий!

Не успели мы опубликовать первую часть статьи, как W3C решили улучшить синтаксис описания именованных линий. Теперь имена линий надо будет указывать не в круглых скобках, а в квадратных. В W3C решили, что это легче читается и лучше совместимо с SASS. Всегда следите за новинками спецификаций по редакторским черновикам!

Неявные именованные линии

У нас есть возможность не только привязывать ячейки к конкретным линиям (как мы видели в прошлой статье), но и привязать эти ячейки к нужным линиям «намертво»!

Всё сразу станет ясно, когда мы рассмотрим простой пример:

HTML-код:

<div class="grid">
  <div>1</div>
  <div>2</div>
  <div>3</div>
</div>

Ну и конечно же CSS:

.grid {
  display: grid;
  width: 500px;
  height: 150px;
  
  grid-template-columns: 2fr 1fr;

  grid-template-rows: [header-top] auto [header-bottom  main-top] 1fr [main-bottom];

  grid-template-areas: "banner    banner"
                       "container menu";
  }
.grid > :nth-child(1) {
    background-color: #FCC;
  
    grid-column-start: banner-start;
    grid-column-end: banner-end;
  
    grid-row-start: header-top;
    grid-row-end: header-bottom;
}

.grid > :nth-child(2) {
    background-color: #CFC;
  
    grid-row-start: header-bottom;
    grid-row-end: main-bottom;
}

.grid > :nth-child(3) {
    background-color: #CCF;
  
    grid-row-start: header-bottom;
    grid-row-end: main-bottom;
}

И результат:

nomamed-line

Результат выглядит довольно просто, HTML-код тоже, но вот на CSS нужно остановиться подробнее, поскольку именно в нём кроется весь фокус неявных именованных линий, а также содержатся незнакомые нам свойства (grid-column-start/grid-row-start и grid-column-end/grid-row-end).

Из прошлой статьи мы помним, что grid-template-columns: 2fr 1fr; означает, что у нас будет две колонки, первая из которых занимает две фракции (2fr), а вторая — одну (1fr).

У трех ячеек нашего грида будут собственные имена: «banner», «container» и «menu». Возможность задавать имена конкретным ячейкам — еще один магический секрет гридов, который мы подробно рассмотрим чуть ниже. А пока примем имена ячеек как данность и посмотрим, как легко и изящно можно привязывать к ним блоки.

Для привязки блоков к конкретным линиям нужно несколько вещей: явные/неявные названия линий и пару волшебных свойств. Явные названия линий уже знакомы нам по предыдущей статье, где мы именовали линии в скобках «[first] 100px [main]…» свойства grid-template-columns. Кстати, в нашем примере мы проделываем аналогичную штуку. А вот с неявными линиями мы сталкиваемся впервые, поэтому берите ручку и записывайте, как ими пользоваться:

В нашем примере 3 именованных ячейки, у каждой ячейки — 4 границы. Эти границы и образуют неявные именованные линии. Имена таких линий получаются из имен ячеек добавлением -start/-end. Поэтому далее берём свойство grid-column-start/grid-column-end и привязываем grid-элемент (в нашем случае — первый) к двум неявным линиям: banner(-start) и banner(-end), соответственно:

.grid > :nth-child(1) {
    grid-column-start: banner-start;
    grid-column-end: banner-end;
}

Только что мы увидели в действии одну из главных фишек Grid Layout — полную независимость визуального порядка от порядка в коде, возможность отображать что угодно где угодно.

Поскольку «banner» занимает две колонки, то левая граница ячейки у нас совпадает с левым краем, а правая — с концом второй колонки грида. И теперь нам осталось только взять свойства grid-column-start/grid-column-end и привязать grid-элемент (в данном случае — первый) к этим колонкам. Я думаю теперь вы уже сами догадались, почему наш элемент стал занимать две колонки? На всякий случай я сделал живой пример.

Реальная польза от привязки элементов

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

Мы не будем трогать вышеприведённую HTML- и CSS-разметку, а обратим внимание на некоторые строки кода в CSS, которые я специально оставил, убрав на время ненужные:

.grid {
  
 grid-template-rows: [header-top] auto [header-bottom  main-top] 1fr [main-bottom];
}

.grid > :nth-child(2) {
    
    grid-row-start: main-top;
    grid-row-end: main-bottom;
}

.grid > :nth-child(3) {
     
    grid-row-start: main-top;
    grid-row-end: main-bottom;
}

Зная, как работают свойства grid-column-start/grid-column-end совсем не трудно догадаться и о назначении свойств grid-row-start/grid-row-end. Если первые отвечают за привязку элемента по горизонтальной оси, то вторые — по вертикальной. Следовательно в нашем случае мы привязали второй и третий элементы к именованным линиям «main-top» и «main-bottom», которые прописаны в скобках свойства grid-template-rows. Чтобы было совсем ясно, на изображении ниже я покажу результат работы кода и отмечу нужные границы.

both-borders

Из картинки ясно, что верхняя граница второго и третьего элементов привязана к линии «main-top», которая находится на границе нижнего края первого элемента (шапки) и верхнего края второго и третьего элементов (за это отвечает grid-row-start: main-top). А нижняя граница второго и третьего элементов привязана к линии «main-bottom», которая находится в самом низу (сделано это с grid-row-end: main-bottom). Кстати, ещё один важный момент: нижний край первого элемента (шапки) находится на одной отметке с верхним краем второго и третьего элементов. Это происходит из-за того, что названия линий «main-top» и «header-bottom» прописаны в одних скобках ([header-bottom main-top]).

И вот наконец-то мы подошли к самой изюминке. Казалось бы, ну привязали и привязали границы элементов к линиям, и что дальше? А дальше вот что. Теперь лёгким движением руки мы можем разделить наши линии [header-bottom main-top] и вставить между ними любое количество ячеек. И фокус в том, что при этом наши grid-элементы всё равно останутся привязанными к своим линиям! Например:

grid-template-rows: [header-top] auto [header-bottom] 50px [main-top] 1fr [main-bottom];

Мы разделили [header-bottom] и [main-top] на два блока, вставив между ними ячейку в 50px. И вот результат:

nomamed-line-2

Несмотря на то, что между первым и вторым/третьим элеменами образовалась пустая ячейка 50px в высоту, наши элементы остались привязанными к своим линиям «намертво», как я и говорил. Только теперь линия [main-top] начинается после пустой ячейки, а линия [header-bottom] — перед ней.

А теперь ещё один пример, возможно, с неочевидным результатом на первый взгляд. Чуть подрихтуем наш CSS, убрав на время всё лишнее:

.grid {
  
  /* добавим ещё одну линию и с пустой ячейкой: 50px [main-top-2] */
  grid-template-rows: [header-top] auto [header-bottom] 50px [main-top] 50px [main-top-2] 1fr [main-bottom];
}

.grid > :nth-child(2) {
    
    grid-row-start: main-top;
    grid-row-end: main-bottom;
}

.grid > :nth-child(3) {
    
    /* верхний край третьего элемента теперь привязан к линии "main-top-2" */
    grid-row-start: main-top-2;
    grid-row-end: main-bottom;
}

И результат:

nomamed-line-3

Здесь почти всё также, как в предыдущем примере, но теперь верхний край третьего элемента ниже верхнего края второго элемента. Так происходит, потому, что мы отвязали верхний край третьего от предыдущей линии (main-top), добавили в код ещё одну (main-top-2) и привязали третий элемент уже к ней. И поскольку между этими линиями расстояние 50px, то третий элемент опустился на это число.

Мне это напомнило абсолютное позиционирование, но только с тем преимуществом, что теперь блоки могут подстраиваться под размеры других элементов, самого грида, а также чётко сопоставлять свои границы с конкретными линиями. Здорово, не правда ли? Но нам надо двигаться дальше…

Практическая магия именованных ячеек: grid-template-areas и его друзья

В этом разделе я предлагаю раскрыть удивительные  возмоности grid-template-areas, и увидеть, насколько просто при помощи этого свойства можно строить сетки, объединять ячейки и крутить/вертеть ими, как нам вздумается.

По традиции начнём с примера. Например, нам нужно сверстать макет, состоящий из шапки, двух колонок и подвала. В данном случае разметка будет такая:

<section id="grid">
  <header>Я header</header>
  <nav>Я nav</nav>
  <main>Я main</main>
  <footer>Я footer</footer>
</section>

А CSS такой:

#grid {
  display: grid;

  grid-template-areas: "head head"
                       "nav  main"
                       "foot .   "
  }
  #grid > header { grid-area: head; }
  #grid > nav    { grid-area: nav; }
  #grid > main   { grid-area: main; }
  #grid > footer { grid-area: foot; }

#grid > * { border: 1px solid; }

Начнём со свойства grid-template-areas. Оно устанавливатся grid-контейнеру. Значение grid-template-areas принимает шаблон из любых строк, идущих в кавычках через пробел. Названия строк могут повторяться и включать в себя даже обычные точки, как видно из CSS ("foot .").

Давайте посмотрим на результат:

areas-1

В нашем случае шаблон свойства grid-template-areas состоит из шести строк, по две строки в трёх парах кавычек (в сумме пять названий и одна точка). Каждая строка, это отдельная ячейка в сетке. Т.к. в первых кавычках у нас прописаны две строки с одинаковым названием, это значит, что первая ячейка будет объединена по горизонтали в две.

Но для того, чтобы grid-элементам понять, какие ячейки будет занимать каждый из них, им нужно указать специальное свойство grid-area. Поскольку мы указали элементу header grid-area: head;, то он будет занимать ячейку под названием «head».

Поскольку с header всё понятно, то с остальными элементами и подавно. Каждый из них занимает по одной ячейке, но что происходит с точкой? Она необходима для того, чтобы сетка «не развалилась», ибо точка представляет собой как бы «невидимую» колонку, которая идёт после колонки footer. Без точки колонки бы просто расположились на отдельных строчках:

areas-2

И очевидно, что если бы вместо точки последнее значение grid-template-areas выглядело бы так «foot foot», то подвал, как и шапка, занял бы две ячейки по горизонтали:

areas-3

А теперь давайте усложним пример. Дабавим кое-что в разметку:

<section id="grid">
  <header>Я header</header>
  <nav>Я nav</nav>
  <aside>Я aside</aside>
  <main>
    <p>Я main</p>
    <p>во мне много текста</p>
  </main>
  <footer>Я footer</footer>
  <address>Я address</address>
</section>

… и в CSS:

#grid {
  display: grid;
  
  grid-template-areas: "left topcenter right"
                       "foot1 center foot2"    
                       "foot1    .   foot2"
  }

  #grid > header { grid-area: topcenter; }
  #grid > nav    { grid-area: left; }
  #grid > aside    { grid-area: right; }
  #grid > main   { grid-area: center; }
  #grid > footer { grid-area: foot1; }
  #grid > address    { grid-area: foot2; }

  #grid > * { border: 1px solid; }

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

areas-4

С верхними ячейками всё ясно, grid-элементы header, nav и aside занимают верхние три, как указано в их свойстве grid-area. Но что происходит с ячейками, которые располагаются ниже? Как вы могли заметить в свойстве grid-template-areas во вторых и третьих пар кавычек есть одинаковые названия строк — foot1 и foot2. Они идут по бокам, и нижние боковые foot1 и foot2 располагаются чётко под верхними foot1 и foot2. И теперь, поскольку в свойстве grid-area для элемента footer мы указали значение foot1, а для элемента address — foot2, их строки объединились и стали занимать две ячейки по вертикали.

Средняя же колонка, состоящая из элемента main (которому выставлено grid-area: center;) и точки (которая прописана в последних кавычках между foot1 и foot2) берут на себя роль средних двух вертикальных ячеек (и являются по сути центральной колонкой), а боковые вынуждены по высоте подстраиваться под них. Следовательно, в нашем случае боковые (объединённые) ячейки могут быть или выше центровины, или одной с ней высоты, но центровина не может быть выше боковых колонок.

Замечание: конечно, я специально в CSS расположил строки так, чтобы визуально они находились друг под другом. Я сделал это для удобочитаемости, поэтому даже если бы пары кавычек располагались не друг под другом, а в одну строку (напр. "foot1 center foot2" "foot1 . foot2"), это не поменяло бы сути. В нашем случае главное, чтобы позиции названий строк в первых кавычках совпадали с позицией названий строк во вторых кавычках.

Но это ещё не всё. Я бы ещё хотел уделить отдельное внимание точке, которая прописана в третьих кавычках. Как и в первом случае, она представляет из себя невидимую виртуальную ячейку (и может схлопываться до нуля), и нужна для того, чтобы наша сетка «не развалилась». Но также вы должны учитывать, что если после последнего элемента в разметке (<address>) мы добавим ещё один элемент (например <p>), то получим следующий результат:

areas-5

Здесь нужно обратить внимание на две вещи. Первое, это то, что место точки в средней колонке занял наш абзац (<p>). Это произошло из-за того, что ячейка (точка) не зарезервирована никаким элементом, а следовательно новый элемент вставляется аккурат в эту пустую безымянную ячейку. И второе, если кого-то удивили непонятно откуда взявшиеся вертикальные отступы у нашего абзаца, то здесь всё ещё проще. Вертикальные отступы — это верхний и нижний внешние отступы (margin), которые по умолчанию выставляются абзацам в браузерах. Поэтому, если мы их уберём при помощи margin:0, то получим ожидаемый результат:

areas-6

Атрибуты colspan и rowspan теперь в CSS при помощи свойства grid-template-areas!

Кстати, ещё хочу сказать пару слов о том, что grid-template-areas также легко может заменить наши старые добрые атрибуты colspan и rowspan, которые мы прописывали для ячеек HTML-таблицы, чтобы объединить нужные нам ячейки по горизонтали или по вертикали. С приходом Grid Layout и grid-template-areas загрязнять разметку и мучится с этими атрибутами больше не придётся! Вы уже видели это на примере ячейки head в предыдущем разделе — она была получена объединением двух ячеек, совсем как с colspan="2" в таблице.

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

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

  1. Денис

    Спасибо большое за статьи.
    С первого раза не осилил, нужно будет еще почитать.

  2. johns

    «живой пример» в конце первого примера не работает. Видлимо потому что не поименовано grid-area

  3. johns

    Извиняюсь, все работает. Я просто пропустил информацию, как включить поддержку grid

    1. SelenIT

      Сейчас можно тестировать еще и в Firefox Beta (45 и новее), там всё, что нужно, включено уже по умолчанию.

  4. Артём

    grid-template-areas также легко может заменить наши старые добрые атрибуты colspan и rowspan, которые мы прописывали для ячеек HTML-таблицы, чтобы объединить нужные нам ячейки по горизонтали или по вертикали. С приходом Grid Layout и grid-template-areas загрязнять разметку и мучится с этими атрибутами больше не придётся!

    То есть, мы табличным элементам устанавливаем display: grid; и сопутствующие свойства, но зачем? Другое дело, когда речь идёт о использовании табличного отображения(CSS-таблиц, так сказать), здесь, к сожалению аналогичных атрибутам colspan / rowspan CSS-свойств не предусмотрено.

    1. Алексей

      Поддерживаю

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

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

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

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