В этом практическом занятии вы узнаете, как создать адаптивный компонент выдвижной боковой навигации для веб-сайта. Мы будем создавать компонент по ходу дела, начиная с HTML, затем CSS и JavaScript.
Ознакомьтесь с записью моего блога Создание компонента Sidenav, чтобы узнать о функциях веб-платформы CSS, выбранных для создания этого компонента.
Настраивать
- Нажмите «Ремикс для редактирования», чтобы сделать проект редактируемым.
- Откройте
app/index.html
.
HTML
Сначала займитесь основами настройки HTML, чтобы было содержимое и несколько полей, с которыми можно работать.
Вставьте следующий HTML-код в тег <body>
.
<aside></aside>
<main></main>
Элемент <aside>
содержит навигационное меню как дополнительный элемент к элементу <main>
, который содержит содержимое основной страницы.
Далее мы заполним эти семантические элементы остальным содержимым страницы.
Добавьте элемент навигации, несколько навигационных ссылок и ссылку закрытия внутри элемента <aside>
.
<aside>
<nav>
<h4>My</h4>
<a href="#">Dashboard</a>
<a href="#">Profile</a>
<a href="#">Preferences</a>
<a href="#">Archive</a>
<h4>Settings</h4>
<a href="#">Accessibility</a>
<a href="#">Theme</a>
<a href="#">Admin</a>
</nav>
<a href="#"></a>
</aside>
Ссылки отлично смотрятся внутри элементов <nav>
, а элементы <nav>
отлично смотрятся в боковых панелях <aside>
. Тем не менее, нам есть над чем работать.
В основном элементе контента добавьте заголовок и статью для семантического хранения содержимого макета.
<main>
<header>
<a href="#sidenav-open" class="hamburger">
<svg viewBox="0 0 50 40">
<line x1="0" x2="100%" y1="10%" y2="10%" />
<line x1="0" x2="100%" y1="50%" y2="50%" />
<line x1="0" x2="100%" y1="90%" y2="90%" />
</svg>
</a>
<h1>Site Title</h1>
</header>
<article>
{put some placeholder content here}
</article>
</main>
В заголовке есть ссылка для открытия меню. Сбоку — кнопка закрытия. Скоро мы начнём отображать и скрывать элементы в зависимости от размера области просмотра.
В элемент <article>
мы вставили предложение-заполнитель. Замените `` своим текстом или вставьте текст, приведённый ниже:
<h2>Totam Header</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cum consectetur, necessitatibus velit officia ut impedit veritatis temporibus soluta? Totam odit cupiditate facilis nisi sunt hic necessitatibus voluptatem nihil doloribus! Enim.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<h3>Subhead Totam Odit</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<h3>Subhead</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
Именно этот контент и его длина станут причиной прокрутки страницы, когда она превысит высоту области просмотра.
На данный момент вы добавили элемент aside с навигацией, ссылками и способом закрытия боковой навигации. Вы также добавили заголовок, способ открытия боковой навигации и статью к основному элементу. Это уже выглядит аккуратно, семантично и довольно актуально, но мы можем сделать его чище и понятнее для всех. Открытую ссылку в боковой навигации можно было бы обозначить более чётко.
Добавьте атрибуты title
и aria-label
к элементу ссылки открытия заголовка:
<a href="#sidenav-open" class="hamburger">
<a href="#sidenav-open" title="Open Menu" aria-label="Open Menu" class="hamburger">
Значок открытия SVG также можно было бы обозначить более чётко. Добавьте следующие атрибуты к SVG внутри элемента открытой ссылки:
<svg viewBox="0 0 50 40">
<svg viewBox="0 0 50 40" role="presentation" focusable="false" aria-label="trigram for heaven symbol">
Ссылка «Закрыть» в боковой панели навигации могла бы быть более чётко обозначена. Добавьте атрибуты title
и aria-label
к элементу ссылки «Закрыть» боковой панели навигации:
<a href="#"></a>
<a href="#" title="Close Menu" aria-label="Close Menu"></a>
CSS
Пора заняться компоновкой элементов. Основной контент и боковая навигация — прямые дочерние элементы тега <body>
, так что с них можно начать.
Добавьте следующий CSS-код в css/sidenav.css
, чтобы элемент <body>
размещал дочерние элементы.
body {
display: grid;
grid: [stack] 1fr / min-content [stack] 1fr;
@media (max-width: 540px) {
& > :matches(aside, main) {
grid-area: stack;
}
}
}
По сути, этот макет гласит: создайте именованный stack
строк со всем содержимым и 2 столбца в этом ряду, второй из которых также будет именованным stack
. Размер первого столбца должен соответствовать минимально необходимому объёму контента, а второй столбец может занимать всё остальное. Затем, если область просмотра ограничена 540px
или меньше, поместите элементы боковой навигации и основного контента в одну строку и один столбец, чтобы они располагались друг над другом в сетке 1x1.
Используя эту адаптивную функциональность наложения в качестве основы, мы теперь можем использовать состояние URL-адресной строки для переключения видимости и стиля перехода боковой навигации.
Обновите элемент <aside>
обратно в app/index.html
:
<aside>
<aside id="sidenav-open">
Это позволяет CSS сопоставлять элемент и хеш URL. Это важно для использования :target
. Теперь идентификатор элемента может совпадать с хешем URL, который мы установим с помощью тегов <a>
.
Кроме того, для упрощения таргетинга JavaScript добавьте идентификаторы ключевых элементов, управляющих боковой навигацией. Сначала добавьте идентификатор к ссылке открытия боковой навигации:
<a href="#sidenav-open" class="hamburger" title="Open Menu" aria-label="Open Menu">
<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">
Затем добавьте идентификатор к ссылке закрытия боковой панели:
<a href="#" title="Close Menu" aria-label="Close Menu"></a>
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>
Это завершает создание адаптивной разметки для макроса <body>
и связывает нас со строкой URL. Продолжим!
Элемент <aside>
тоже имеет аккуратную структуру. У него два дочерних элемента: <nav>
— выдвижной компонент, похожий на бумажный лист, и закрывающий элемент <a>
, который устанавливает URL-адрес в #
. Ссылка не видна справа от выдвижного элемента навигации; это сделано для того, чтобы пользователь мог «кликнуть» по визуальному компоненту, чтобы закрыть его.
Добавьте следующий CSS в css/sidenav.css
:
#sidenav-open {
display: grid;
grid-template-columns: [nav] 2fr [escape] 1fr;
}
Я считаю, что соотношение сторон и названия здесь были действительно удачным решением, поскольку сетка могла бы проявить себя во всей красе и предоставить дизайнеру широкие возможности контроля.
Далее мне нужно настроить условное наложение основного контента и сохранить позицию при любой прокрутке документа. Это отлично подходит для position: sticky
и некоторых overscroll-behavior
.
Добавьте следующие стили для боковой навигации:
#sidenav-open {
display: grid;
grid-template-columns: [nav] 2fr [escape] 1fr;
@media (max-width: 540px) {
position: sticky;
top: 0;
max-height: 100vh;
overflow: hidden auto;
overscroll-behavior: contain;
visibility: hidden; /* not keyboard accessible when closed */
}
}
Эти стили гарантируют, что боковая навигация будет соответствовать высоте области просмотра, будет прокручиваться вертикально и содержать прокрутку. Что очень важно, она скрывает элемент. По умолчанию, если область просмотра составляет 540px
или меньше, боковая навигация скрывается. Разве что!
Добавьте псевдоселектор :target
к элементу #sidenav-open
:
#sidenav-open {
@media (max-width: 540px) {
&:target {
visibility: visible;
}
}
}
Если идентификатор этого элемента и адресная строка совпадают, установите для видимости visibility
visible
. Откройте боковое меню после прокрутки страницы или попробуйте прокрутить страницу, пока открыта боковая навигация. Что вы думаете?
Добавьте следующий CSS-код в конец app/sidenav.css
:
#sidenav-button,
#sidenav-close {
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
user-select: none;
touch-action: manipulation;
@media (min-width: 540px) {
display: none;
}
}
Эти стили определяют наши кнопки открытия и закрытия, определяют их стили нажатия и прикосновения, а также скрывают их, если размер области просмотра составляет 540px
или больше.
Для пущего эффекта давайте добавим CSS-преобразования с уважительной доступностью. Добавьте следующий CSS-код в css/sidenav.css
:
#sidenav-open {
--easeOutExpo: cubic-bezier(0.16, 1, 0.3, 1);
--duration: .6s;
...
@media (max-width: 540px) {
...
transform: translateX(-110vw);
will-change: transform;
transition:
transform var(--duration) var(--easeOutExpo),
visibility 0s linear var(--duration);
&:target {
visibility: visible;
transform: translateX(0);
transition: transform var(--duration) var(--easeOutExpo);
}
}
@media (prefers-reduced-motion: reduce) {
--duration: 1ms;
}
}
Добавьте немного JavaScript
Клавиша Escape
должна закрывать меню. Добавьте этот JS-код в js/index.js
:
const sidenav = document.querySelector('#sidenav-open');
sidenav.addEventListener('keyup', e => {
if (e.code === 'Escape') {
document.location.hash = '';
}
});
Этот код отслеживает событие нажатия клавиши на элементе sidenav. Если это Escape
, хеш URL становится пустым, что приводит к выходу sidenav.
Следующий фрагмент UX JS-кода — управление фокусом. Я хочу упростить открытие и закрытие, поэтому жду, пока боковая навигация завершит какой-либо переход, а затем сверяю его с хешем URL, чтобы определить, находится ли она в фокусе или нет. Затем я использую JavaScript, чтобы установить фокус на кнопку, дополняющую ту, которую пользователь только что нажал.
Добавьте следующий JavaScript в js/index.js
:
const closenav = document.querySelector('#sidenav-close');
const opennav = document.querySelector('#sidenav-button');
sidenav.addEventListener('transitionend', e => {
if (e.propertyName !== 'transform') {
return;
}
const isOpen = document.location.hash === '#sidenav-open';
isOpen
? closenav.focus()
: opennav.focus();
});
Попробуйте это
- Для предварительного просмотра сайта нажмите «Просмотреть приложение» . Затем нажмите «Полный экран».
.
Заключение
Вот и всё, что мне нужно было для этого компонента. Можете смело его дорабатывать, использовать JavaScript-состояние вместо URL и вообще, делать его своим! Всегда есть что добавить и охватить новые варианты использования.
Откройте css/brandnav.css
чтобы ознакомиться со стилями, не связанными с макетом, которые я применил к этому компоненту. Я не считал, что это важно для набора функций, на котором я сосредоточился, и надеялся, что разделение стилей и макета будет способствовать копированию и вставке. Возможно, там вы найдете для себя больше полезного!
Как сделать выдвижные адаптивные компоненты боковой навигации? У вас когда-нибудь было больше одного компонента, например, по одному с каждой стороны? Я бы с удовольствием показал ваше решение в видео на YouTube. Обязательно напишите мне в Твиттере или оставьте комментарий с вашим кодом на YouTube — это поможет всем!