Прилипающие CSS Grid элементы
position: sticky
не работает с CSS Grid.Проблема: position sticky не работает
Давайте начнём с основной проблемы, с которой вы, вероятно, столкнётесь, потому что сначала не всегда очевидно, почему ваш grid-элемент не прилипает, position sticky не работает.
Допустим, мы собираем шаблон макета для статей о различных сладостях. У нас есть заголовок в левом столбце grid макета, и мы установили его sticky
:
article {
display: grid;
grid-template-columns: 20em 1fr;
grid-gap: 4em;
}
.title {
position: sticky;
top: 2rem;
}
К сожалению, наш заголовок не прилипает к верхней части области просмотра, а вместо этого прокручивается с остальным текстом. Посмотреть пример на CodePen.
Если мы добавим границы (border: 10px solid;
) к нашему sticky
заголовку (или используем инспектор GSS Grid), наша основная проблема станет более очевидной. Посмотреть пример на CodePen.
Алгоритм изменения размера элементов CSS Grid эффективно определяет размер grid элемента (нашего sticky
заголовка), что бы заполнить высоту grid слота, в который он был помещён. Мы не видим ни каких эффектов sticky
, потому что его вычисленная высота такая же, как и у содержимого в соседнем grid слоте.
Мелкие подробности о размерах
Сейчас всё станет скучным, просто перейдите к раздела "Как это исправить", если нужно только решение проблемы!
Поскольку мы не использовали какие-либо CSS свойства определяющие размер наших grid элементов (скоро мы к ним вернёмся), CSS Grid по умолчанию использовал свой "нормальный" алгоритм определения размера при определении размера sticky
заголовка.
Наш заголовок h1
...
- Это не заменяемый элемент, такой как
img
,video
,embed
,iframe
и т.д. У них есть особые правила определения размера - Не имеет "предпочтительного соотношения сторон". Такое внутреннее соотношение сторон может быть установлено для определённых элементов в таблице стилей пользовательского агента или с помощью свойства
aspect-ratio
...поэтому алгоритм вернулся к "растянутой" схеме определения размеров. В основном это гарантирует, что размер блока в этом измерении установлен таким образом, что бы заполнять контейнер макета в котором он находится (или упрощая grid слот).
Как это исправить
Один из способов решения этой проблемы — указать внешний размер высоты для нашего sticky
заголовка, например: height: 10em, max-height: 50vh
, или что-то подобное. Однако указывать конкретную высоту для нашего контента это халтура, на м не нужна конкретная высота, мы хотим, что бы вычисленная высота была достаточной для размещения содержимого элемента. Вместо этого мы можем изменить способ выравнивания этого элемента по grid слоту.
Мы можем указать одно из следующих свойств:
align-self
: это свойство, если оно указано вsticky
заголовке, сообщает элементу, как выровнять себя по направлению блока в grid слоте, в котором он находится.align-self: start
отлично пойдёт для нашего примера, поскольку он выравнивается, но "начальному" краю grid слота (в данном контексте, по верхнему краю).align-items
: это свойство, если оно указано вarticle
(наш grid родитель), сообщает каждому grid элементу, как выровнять себя по направлению блока grid слота в котором он находится.
place-self
иplace-items
так же будут работать; они обрабатывают обе оси, ось блока (или ось столбца) и внутренней оси (или ось строки)
Было бы неплохо выровнять базовую линию текста нашего sticky
заголовка и базовую линию текста первого абзаца. Если мы установим align-items: baseline;
в article
, которая заставляет алгоритм определения размера использовать схему "подгонки содержимого". В этом есть некоторая сложность, но в нашем случае алгоритм использовал "максимальный размер содержимого" нашего grid элемента или "наименьший размер, который блок может занимать по этой оси, при этом всё ещё вписываясь в его содержимое".
Поскольку размер sticky
заголовка теперь изменяется в соответствии с его содержимым, мы получаем поведение position: sticky
, которое нам нужно. Посмотреть пример на CodePen.
Наши обновлённые стили:
article {
display: grid;
grid-template-columns: 20em 1fr;
grid-gap: 4em;
/* The new line we added! */
align-items: baseline;
}
.title {
position: sticky;
top: 2rem;
}
Вот и всё! Надеюсь это поможет вам выйти из sticky
ситуации.