Прокрутка
Прокрутку браузер делает сам, но CSS умеет её настроить: сделать переход к якорю плавным, остановить карусель точно на слайде, не дать прокрутке «перетечь» на страницу. В примерах прокручивается не окно, а блок с overflow: auto из урока о переполнении — свойства работают одинаково.
Плавная прокрутка
Ссылка вида <a href="#prices"> из урока о ссылках мгновенно перебрасывает к нужному месту. scroll-behavior: smooth делает этот переход плавным. В примере вместо ссылок кнопки: они делают такой же переход из JavaScript, а container: 'nearest' просит прокрутить только ближайший блок, а не всю страницу урока:
<style>
.box { height: 140px; overflow: auto; scroll-behavior: smooth; border: 1px solid silver; }
.box p { height: 120px; margin: 0; padding: 8px; }
</style>
<button onclick="document.querySelector('#end').scrollIntoView({ container: 'nearest' })">Вниз</button>
<button onclick="document.querySelector('#start').scrollIntoView({ container: 'nearest' })">Наверх</button>
<div class="box">
<p id="start">Начало</p>
<p>Середина</p>
<p id="end">Конец</p>
</div>Для всей страницы правило пишут так: html { scroll-behavior: smooth; }.
Якорь под липкой шапкой
Если шапка прилипает к верху (position: sticky или fixed), при переходе к якорю заголовок раздела прячется под ней. scroll-margin-top оставляет над целью отступ:
<style>
.page { height: 160px; overflow: auto; border: 1px solid silver; }
.page header { position: sticky; top: 0; padding: 8px; background: steelblue; color: white; }
.page section { height: 150px; padding: 0 8px; scroll-margin-top: 44px; }
</style>
<button onclick="document.querySelector('#delivery').scrollIntoView({ container: 'nearest' })">К доставке</button>
<div class="page">
<header>Магазин</header>
<section><h3>Каталог</h3></section>
<section id="delivery"><h3>Доставка</h3></section>
<section><h3>Контакты</h3></section>
</div>Отступ равен высоте шапки с небольшим запасом. Уберите scroll-margin-top — заголовок «Доставка» окажется под шапкой.
Карусель: scroll-snap
Привязка прокрутки (scroll snap) останавливает прокрутку точно на элементе, а не где придётся. Контейнеру задают scroll-snap-type, элементам — точку привязки scroll-snap-align. Прокрутите ленту вбок:
<style>
.carousel { display: flex; gap: 8px; overflow-x: auto; scroll-snap-type: x mandatory; }
.slide { flex: 0 0 80%; height: 100px; display: grid; place-items: center; scroll-snap-align: center; color: white; font-size: 24px; }
</style>
<div class="carousel">
<div class="slide" style="background: tomato;">1</div>
<div class="slide" style="background: seagreen;">2</div>
<div class="slide" style="background: steelblue;">3</div>
</div>Где бы вы ни отпустили ленту, она доедет до ближайшего слайда и выровняет его по центру.
Значения привязки
| Свойство | Значение | Что делает |
|---|---|---|
scroll-snap-type | x или y | ось, по которой работает привязка |
mandatory | всегда останавливаться на элементе | |
proximity | только если остановились рядом с ним | |
scroll-snap-align | start, center, end | какой край элемента выравнивать |
overscroll-behavior
Когда вложенный блок прокручен до конца, следующие движения колёсика начинают прокручивать страницу. Это называют цепочкой прокрутки. overscroll-behavior: contain останавливает её на границе блока:
<style>
.lists { display: flex; gap: 8px; }
.list { flex: 1; height: 110px; overflow: auto; border: 1px solid silver; }
.list p { margin: 0; padding: 6px 8px; }
.contain { overscroll-behavior: contain; }
</style>
<div class="lists">
<div class="list"><p>Обычный список</p><p>Анна</p><p>Борис</p><p>Вера</p><p>Глеб</p><p>Дина</p></div>
<div class="list contain"><p>contain</p><p>Анна</p><p>Борис</p><p>Вера</p><p>Глеб</p><p>Дина</p></div>
</div>Прокрутите каждый список колёсиком до конца и продолжайте: в левом начнёт прокручиваться страница урока, в правом — нет. Так делают выпадающие меню, чаты и модальные окна.
Частая ошибка
scroll-snap-type пишут элементам, а не контейнеру, — и привязка не работает. Тип привязки задаётся тому, кто прокручивается, а элементам — только scroll-snap-align:
<style>
.carousel { display: flex; gap: 8px; overflow-x: auto; }
.slide { flex: 0 0 80%; height: 80px; background: gold; scroll-snap-type: x mandatory; scroll-snap-align: start; }
</style>
<div class="carousel">
<div class="slide">Лента</div>
<div class="slide">останавливается</div>
<div class="slide">где попало</div>
</div>