Создание сетки с помощью CSS Grid
В недавнем проекте я наткнулся на изящный способ создания макета сетки, используя CSS Grid и немного javascript. Сначала я думал об использовании чего-то вроде Masonry.js или Isotope, но это было немного излишне. Что хорошего в этом подходе, он очень гибкий и не опирается ни на какие фреймворки! Я постараюсь сделать этот пост коротким и приятным, чтобы вы могли сами приступить к работе и насладиться этим трюком.
Давайте начнем с добавления HTML в файл index.html.
Здесь мы создали простую сетку с некоторыми элементами. Примечание: padding-top и lazyloaded предназначены для предотвращения скачка контента при загрузке ваших изображений.
Теперь давайте добавим немного CSS
.grid { display: grid; grid-row-gap: 0px; grid-column-gap: 40px; grid-template-columns: repeat( auto-fit, minmax(300px, var(--template-columns, 1fr)) ); grid-auto-rows: 5px; width: 80%; max-width: 1080px; margin: 0 auto; padding-bottom: 40px; } .grid-item { grid-row-end: var(--row-span, span 40) padding-bottom: 40px } img { width: 100%; height: auto } @media (max-width:800px) { .grid { width: 90%; grid-template-columns: 1fr; grid-auto-rows: auto } .grid-item { grid-row-end: auto } }
По сути, то, что мы здесь делаем с .grid
, говорит о том, что мы хотим отобразить адаптивную сетку элементов. minmax(300px, var(-template-columns, 1fr)) делает для нас большую работу, создавая повторяющиеся столбцы с минимальной шириной 300px и максимальной шириной 1fr (fr - дробная единица), когда ширина столбца падает ниже минимального значения, макет перетасовывается, а количество столбцов уменьшается.
Здесь следует отметить некоторые вещи, grid-auto-rows: 5px
которые позволят нам создать макет каменной кладки. Некоторые стилистические предпочтения, которые у меня есть, это использовать grid-gap-columns
и padding-bottom
чтобы создавать промежутки между элементами. Причина этого в том, что я обнаружил, что интервал стал заметно несовместимым, если grid-gap-rows
также используется.
const grid = document.querySelector('.grid'); const gridItems = document.querySelectorAll('.grid-item'); const rowSize = 5; const setColumns = value => grid.style.setProperty('--template-columns', value); gridItems.length <= 2 ? setColumns(`0.333fr`) : setColumns(`1fr`); const positionGridItems = () => { gridItems.forEach((x, i) => { if (document.body.clientWidth < 711) { x.style = '' return } else { const rowSpan = Math.ceil( gridItems.offsetHeight) / rowSize ) x.style.setProperty('--row-span', `span ${rowSpan}`) } }); }; function debounce(func, wait, immediate) { var timeout return function() { var context = this, args = arguments var later = function() { timeout = null if (!immediate) func.apply(context, args) } var callNow = immediate && !timeout clearTimeout(timeout) timeout = setTimeout(later, wait) if (callNow) func.apply(context, args) } }; document.addEventListener('DOMContentLoaded', event => { positionGridItems() }); window.addEventListener('resize', debounce(positionGridItems, 20));
Теперь здесь происходит вся магия. Сначала мы получаем элементы DOM сетки и его элементов, мы устанавливаем значение rowSize
(которое эквивалентно значению grid-auto-row в нашем файле CSS). Мы создаем функцию setColumns
, которая изменяет переменную grid-template-columns
, если имеется менее трех элементов (полная строка), чтобы поддерживать размеры элементов сетки.
В нашей функции positionGridItems
мы выполняем цикл gridItems
, если в браузере удаляются встроенные стили размером менее 711 пикселей. Что-нибудь выше 711px мы получаем высоту каждого элемента и делим его на наше значение rowSize
. Затем мы устанавливаем нашу переменную --row-span
, которая является значением нашего свойства grid-row-ends
. И это почти вся тяжелая работа! Затем мы переходим к созданию функции debounce (взятой из поста Дэвида Уолша) и затем вызываем нашу функцию positionGridItems
для события DOMContendLoaded
. Мы вызываем ее на событии DOMContentLoaded
, чтобы предотвратить мерцание контента. Затем, чтобы завершить все это, мы добавляем слушатель debounce событий для повторного вызова функции при изменении размера браузера.