Как построить отзывчивую панель навигации с помощью Flexbox

Как построить отзывчивую панель навигации с помощью Flexbox

Содержание
  1. Отзывчивая панель навигации Flexbox (видеоурок)
  2. Flexbox идеально подходит для отзывчивой навигации
  3. 1. Создайте HTML
  4. 2. Добавьте базовый стиль
  5. 3. Начните с мобильной навигации
  6. 4. Стиль подменю
  7. 5. Добавьте функциональность переключения с помощью JavaScript
  8. 6. Добавление функциональности выпадения с помощью JavaScript
  9. 7. Создание меню планшета
  10. 8. Создание меню рабочего стола
  11. 9. Позволяем пользователям закрывать подменю, щелкая в любом месте страницы
  12. Вы создали отзывчивую панель навигации с помощью Flexbox и JavaScript!

Отзывчивая панель навигации Flexbox (видеоурок)

Вот полная видеоверсия этого урока.

Flexbox идеально подходит для отзывчивой навигации

Flexbox - это универсальный модуль верстки, с помощью которого мы можем создавать одномерные макеты, требующие гибкости, например, отзывчивые меню. Используя свойства flexbox по упорядочиванию, выравниванию и размеру, мы можем создавать навигационные панели, которые адаптируют свои макеты к размеру области просмотра, сохраняя при этом логичность и доступность HTML-контура.

В этом уроке мы рассмотрим, как создать отзывчивую панель навигации с помощью flexbox. Наша flexbox-навигация будет иметь три разных макета в зависимости от размера области просмотра:

  1. мобильный макет, в котором по умолчанию будут видны только логотип и кнопка переключения, а пользователи смогут открывать и закрывать меню с помощью переключения,
  2. планшетный макет, в котором мы покажем две кнопки призыва к действию между логотипом и кнопкой переключения в состоянии по умолчанию, а остальная часть меню останется переключаемой,
  3. настольный макет, в котором все пункты меню, кроме кнопки переключения, будут видны на экране.

Мы будем использовать медиа-запросы для определения размера области просмотра браузера пользователя. Наша отзывчивая панель навигации будет ориентирована на мобильные устройства, поэтому сначала мы создадим мобильный макет. Затем мы добавим CSS для планшетов и настольных компьютеров с помощью медиазапросов min-width.

В навигационной панели также будет присутствовать выпадающее подменю на основе JavaScript, которое открывается и закрывается, когда пользователь нажимает на родительский пункт меню.

Вот как меню будет выглядеть на мобильных устройствах:

Мобильное меню с flexbox
Мобильное меню с flexbox
Мобильное меню с flexbox
Мобильное меню с flexbox
Мобильное меню с flexbox
Мобильное меню с flexbox

А вот версия для планшетов:

Планшетное меню с flexbox
Планшетное меню с flexbox
Планшетное меню с flexbox
Планшетное меню с flexbox
Планшетное меню с flexbox
Планшетное меню с flexbox

А вот как это будет выглядеть на рабочем столе:

Меню рабочего стола с flexbox
Меню рабочего стола с flexbox
Меню рабочего стола с flexbox
Меню рабочего стола с flexbox
Меню рабочего стола с flexbox
Меню рабочего стола с flexbox

Вы также можете протестировать, форкнуть и поиграть с интерактивной демонстрацией на CodePen:

1. Создайте HTML

HTML представляет собой простой список <ul>, как вы можете видеть ниже. Класс .menu будет гибким контейнером, а элементы списка - гибкими элементами. Их порядок будет адаптироваться к размеру области просмотра устройства пользователя. Например, кнопки Log In и Sign Up будут на первом месте на мобильных устройствах, но будут отображаться в конце меню на десктопе. Мы добьемся этого эффекта, используя свойства flexbox’s ordering properties.

<nav>
	<ul class="menu">
		<li class="logo"><a href="#">Creative Mind Agency</a></li>
		<li class="item"><a href="#">Home</a></li>
		<li class="item"><a href="#">About</a></li>
		<li class="item has-submenu">
			<a tabindex="0">Services</a>
			<ul class="submenu">
				<li class="subitem"><a href="#">Design</a></li>
				<li class="subitem"><a href="#">Development</a></li>
				<li class="subitem"><a href="#">SEO</a></li>
				<li class="subitem"><a href="#">Copywriting</a></li>
			</ul>
		</li>
		<li class="item has-submenu">
			<a tabindex="0">Plans</a>
			<ul class="submenu">
				<li class="subitem"><a href="#">Freelancer</a></li>
				<li class="subitem"><a href="#">Startup</a></li>
				<li class="subitem"><a href="#">Enterprise</a></li>
			</ul>
		</li>
		<li class="item"><a href="#">Blog</a></li>
		<li class="item"><a href="#">Contact</a></li>
		<li class="item button"><a href="#">Log In</a></li>
		<li class="item button secondary"><a href="#">Sign Up</a></li>
		<li class="toggle">
			<a href="#"><i class="fas fa-bars"></i></a>
		</li>
	</ul>
</nav>

You have probably noticed that menu items с подменю (”Услуги” и ”Планы”) имеют тег <a> без атрибута href. Мы делаем это потому, что эти ”пустые” пункты родительского меню не ведут ни на какую другую страницу - они просто открывают и закрывают подменю. Использование тега якоря без атрибута href разрешено и предотвращает подпрыгивание страницы на маленьких экранах, когда пользователь нажимает на пустой пункт меню, чтобы открыть или закрыть подменю.

Мы также добавляем атрибут [tabindex="0"](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex) к элементам <a> без атрибута href. Это происходит потому, что пустые теги <a> исключаются из стандартного порядка табуляции, поэтому нам нужно вернуть их в порядок табуляции с помощью атрибута tabindex, чтобы сохранить меню доступным для клавиатуры.

Примечание: кнопка переключения в конце списка использует иконку Font Awesome. Чтобы демонстрация работала, вам нужно добавить библиотеку Font Awesome в раздел <head> HTML-документа из CDN, используя приведенный ниже код. (Если вы хотите, чтобы меню работало в автономном режиме, вам нужно разместить Font Awesome локально).

<link
	rel="stylesheet"
	href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css"
/>

2. Добавьте базовый стиль

Для базового стиля я установил некоторые значения и цвета по умолчанию, однако вы можете использовать и свои собственные правила стиля:

/* Базовый стиль */
* {
	box-sizing: border-box;
	padding: 0;
	margin: 0;
}
body {
	font-family: sans-serif;
	font-size: 16px;
}
nav {
	background: #222;
	padding: 0 15px;
}
a {
	цвет: белый;
	text-decoration: none;
}
.menu,
.submenu {
	list-style-type: none;
}
.logo {
	font-size: 20px;
	padding: 7.5px 10px 7.5px 0;
}
.item {
	padding: 10px;
}
.item.button {
	padding: 9px 5px;
}
.item:not(.button) a:hover,
.item a:hover::after {
	color: #ccc;
}

3. Начните с мобильной навигации

Поскольку наша навигация будет ориентирована на мобильные устройства, мы начнем с мобильного макета. Большинство отзывчивых flexbox-меню используют колоночный макет для мобильных устройств, так как пункты меню можно быстро упаковать друг под другом, добавив правило flex-direction: column; к flex-контейнеру. Несмотря на то что это отличное решение, в нашем примере мы его использовать не будем.

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

Мобильный макет
Мобильный макет
Мобильный макет
Мобильный макет
Мобильный макет
Мобильный макет

CSS-хитрость заключается в том, что мы делаем обычные пункты меню, такие как Home и About, растянутыми по всему контейнеру с помощью правила width: 100%;. Таким образом, flexbox отобразит их друг под другом, в то время как логотип и тумблер сохранят свои естественные размеры и расположатся на вершине навигационной панели в одном ряду.

В CSS ниже мы также используем свойства [justify-content](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content) и [align-items](https://developer.mozilla.org/en-US/docs/Web/CSS/align-items) для выравнивания элементов flexbox по горизонтали и вертикали. Кроме этого, мы скрываем элементы .item с помощью правила display: none;. Пункты меню будут раскрываться только тогда, когда пользователь нажмет на кнопку переключения. Класс .active отсутствует в HTML-коде, мы добавим его динамически с помощью JavaScript.

/* Мобильное меню */
.menu {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: center;
}
.menu li a {
	display: block;
	padding: 15px 5px;
}
.menu li.subitem a {
	padding: 15px;
}
.toggle {
	порядок: 1;
	font-size: 20px;
}
.item.button {
	порядок: 2;
}
.item {
	порядок: 3;
	ширина: 100%;
	text-align: center;
	display: none;
}
.active .item {
	display: block;
}
.button.secondary {
	/* разделитель между кнопками и ссылками меню */
	border-bottom: 1px #444 solid;
}

Как вы можете видеть выше, мы также изменили порядок пунктов меню с помощью свойства [order](https://developer.mozilla.org/en-US/docs/Web/CSS/order). Наш HTML-контур следует логическому порядку. Именно так мы хотим, чтобы пользователи программ для чтения с экрана и боты поисковых систем переходили по меню.

Однако в мобильном макете мы хотим отобразить логотип и кнопку переключения в верхней части меню. Мы также хотим отобразить две кнопки призыва к действию (”Войти” и ”Зарегистрироваться”) перед обычными пунктами меню. Поэтому мы установили следующий порядок:

  • .logo получает order: 0;, так как это будет первый элемент (однако, поскольку это значение по умолчанию для order, нам не нужно добавлять его в CSS),
  • .toggle получит значение 1, так как он идет сразу после .logo,
  • .item.button, принадлежащий кнопкам Log In и Sign Up, получает 2,
  • а .item, принадлежащий остальным пунктам меню, получает 3.

4. Стиль подменю

Поскольку это навигация, ориентированная на мобильные устройства, мы будем оформлять подменю с учетом мобильных экранов. Это отличный прием, так как создать удобное подменю для маленьких экранов обычно сложнее, чем для больших. Затем мы можем использовать тот же макет подменю и для экранов планшетов. Для настольных компьютеров нам нужно будет только изменить расположение подменю.

По умолчанию для подменю установлено значение display: none;, и оно будет открываться только тогда, когда пользователь щелкнет по родительскому пункту меню. Мы добавим необходимую функциональность JavaScript в следующих двух шагах, прежде чем перейдем к меню планшета.

/* Подменю на мобильных экранах */
.submenu {
	display: none;
}
.submenu-active .submenu {
	display: block;
}
.has-submenu i {
	font-size: 12px;
}
.has-submenu > a::after {
	font-family: 'Font Awesome 5 Free';
	font-size: 12px;
	line-height: 16px;
	font-weight: 900;
	content: '\f078';
	цвет: белый;
	padding-left: 5px;
}
.subitem a {
	padding: 10px 15px;
}
.submenu-active {
	background-color: #111;
	border-radius: 3px;
}

Как вы можете видеть выше, теперь мы добавляем иконки Font Awesome с помощью CSS, а не HTML. Эти иконки, добавленные с помощью псевдоэлемента ::after, будут представлять собой маленькие стрелки вниз, отображаемые рядом с каждым пунктом меню, имеющим подменю.

Если вы помните, в шаге 1 мы добавили иконку Font Awesome для кнопки переключения с помощью HTML. Это связано с тем, что кнопка переключения будет нацелена на JavaScript, поэтому она должна присутствовать в DOM. Однако стрелки вниз здесь - это просто элементы стиля, которые указывают на наличие подменю. Поскольку от них не зависит никакой функциональности, лучше добавить их с помощью CSS.

5. Добавьте функциональность переключения с помощью JavaScript

Мы настроим функцию переключения, добавив слушателя события click для кнопки переключения, которая открывает и закрывает меню на мобильных устройствах. В коде JavaScript мы будем использовать синтаксис ES6, который дает нам доступ к нотациям const и let, циклу for...of и уже имеет хорошую браузерную поддержку.

Для пользовательского JavaScript создайте пустой файл script.js и добавьте его в HTML перед закрывающим тегом `:

<script src="script.js"></script>

А вот код JavaSript, отвечающий за функциональность переключения:

const toggle = document.querySelector('.toggle');
const menu = document.querySelector('.menu');
/* Переключение мобильного меню */
function toggleMenu() {
	if (menu.classList.contains('active')) {
		menu.classList.remove('active');

		// добавляет иконку меню (гамбургера)
		toggle.querySelector('a').innerHTML = '<i class='fas fa-bars'></i>';
	} else {
		menu.classList.add('active');

		// добавляет иконку закрытия (x)
		toggle.querySelector('a').innerHTML = '<i class='fas fa-times'></i>';
	}
}
/* Слушатель событий */
toggle.addEventListener('click', toggleMenu, false);
  1. Сначала мы выбираем меню и кнопку переключения с помощью метода [querySelector()](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector), чтобы иметь к ним доступ с помощью JavaScript.
  2. Затем мы добавляем пользовательскую функцию toggleMenu(), которая будет вызываться при нажатии на кнопку переключения.
  3. Наконец, мы добавляем слушателя событий, который будет прослушивать событие щелчка, используя метод [addEventListener()](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener).

6. Добавление функциональности выпадения с помощью JavaScript

Теперь, когда пользователь нажимает на кнопку переключения, меню активируется и деактивируется, однако подменю остается скрытым. Мы добавим эту функциональность с помощью следующего JavaScript:

const items = document.querySelectorAll('.item');
/* Активация подменю */
function toggleItem() {
	if (this.classList.contains('submenu-active')) {
		this.classList.remove('submenu-active');
	} else if (menu.querySelector('.submenu-active')) {
		menu.querySelector('.submenu-active').classList.remove('submenu-active');
		this.classList.add('submenu-active');
	} else {
		this.classList.add('submenu-active');
	}
}
/* Слушатели событий */
for (let item of items) {
	if (item.querySelector('.submenu')) {
		item.addEventListener('click', toggleItem, false);
		item.addEventListener('keypress', toggleItem, false);
	}
}

Здесь мы добавляем класс .submenu-active к каждому пункту меню с подменю, когда пользователь нажимает на него.

  1. Сначала мы выбираем все пункты меню с помощью метода [querySelectorAll()](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll), который возвращает список узлов (а не один элемент, как querySelector()).
  2. В пользовательской функции toggleItem() мы добавляем и удаляем .submenu-active to/f из щелкнутого элемента. Обратите внимание, что в блоке else if мы удаляем класс из всех остальных пунктов меню, которые были открыты ранее. Таким образом, не получится так, что два подменю будут открыты одновременно, так как на рабочем столе они могут перекрывать друг друга.
  3. Наконец, мы просматриваем список классов items с помощью цикла [for...of](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of). В блоке if мы добавляем два слушателя событий для пунктов меню, имеющих подменю: один для события click для обычных пользователей, которые заходят в меню с помощью щелчка или касания, и один для события keypress для пользователей клавиатуры.

7. Создание меню планшета

Мы создадим планшетный макет с помощью медиазапроса min-width. На планшете по умолчанию будут видны четыре пункта меню: логотип, две кнопки призыва к действию (”Войти” и ”Зарегистрироваться”) и тумблер. Чтобы сделать все красиво, наш CSS будет:

  • изменит порядок пунктов меню, чтобы адаптировать макет к видовым экранам планшетов,
  • выровнять пункты (см. пояснения ниже),
  • сделать кнопки Войти и Зарегистрироваться похожими на настоящие кнопки (в мобильном макете они выглядят как ссылки, поскольку являются частью выпадающего списка с возможностью переключения).

В коде:

/* Меню планшета */
@media all and (min-width: 700px) {
	.menu {
		justify-content: center;
	}
	.logo {
		flex: 1;
	}
	.item.button {
		ширина: авто;
		порядок: 1;
		display: block;
	}
	.toggle {
		flex: 1;
		text-align: right;
		порядок: 2;
	}
	/* Кнопка вверх с экрана планшета */
	.menu li.button a {
		padding: 10px 15px;
		margin: 5px 0;
	}
	.button a {
		background: #0080ff;
		border: 1px royalblue solid;
	}
	.button.secondary {
		border: 0;
	}
	.button.secondary a {
		фон: прозрачный;
		border: 1px #0080ff solid;
	}
	.button a:hover {
		text-decoration: none;
	}
	.button:not(.secondary) a:hover {
		фон: королевский синий;
		border-color: darkblue;
	}
}

В планшетном макете пункты меню выровнены по-другому. Если вы посмотрите на четыре видимых пункта меню, то увидите, что две кнопки отображаются в центре, а логотип и тумблер сдвинуты к левому и правому краю контейнера:

Отзывчивое меню планшета
Отзывчивое меню планшета
Отзывчивое меню планшета
Отзывчивое меню планшета
Отзывчивое меню планшета
Отзывчивое меню планшета

Мы можем добиться этого эффекта с помощью CSS-правила flex: 1;. Свойство [flex](https://developer.mozilla.org/en-US/docs/Web/CSS/flex) - это сокращение для flex-grow, flex-shrink и flex-basis. Оно может существовать с различными комбинациями значений. Когда оно объявлено только с одним значением, оно относится к flex-grow, а flex-shrink и flex-basis сохраняют свои значения по умолчанию.

В приведенном выше CSS мы добавили правило flex: 1; к элементам .logo и .toggle. Таким образом, мы можем сообщить браузеру, что если на экране есть свободное пространство, то мы хотим разделить его между этими двумя элементами. Поскольку кнопки Log In и Sign Up сохраняют стандартное значение 0 для flex-grow, они ничего не получат от дополнительного пространства. Поэтому они останутся в центре контейнера, поскольку придерживаются правила justify-content: center;, установленного для гибкого контейнера.

8. Создание меню рабочего стола

Меню рабочего стола скрывает тумблер, возвращает исходный порядок и естественную ширину каждого элемента, а также изменяет положение подменю.

Важно помнить, что правила, характерные для планшетов, применяются и к меню рабочего стола. Это связано с тем, что здесь ширина области просмотра больше, чем 700px и 960px, поэтому действуют оба медиазапроса. Итак, .logo сохраняет свое свойство flex: 1; и сдвигает остальные элементы в конец контейнера.

/* Меню рабочего стола */
@media all and (min-width: 960px) {
	.menu {
		align-items: flex-start;
		flex-wrap: nowrap;
		background: none;
	}
	.logo {
		order: 0;
	}
	.item {
		порядок: 1;
		позиция: относительная;
		display: block;
		ширина: auto;
	}
	.button {
		порядок: 2;
	}
	.submenu-active .submenu {
		display: block;
		position: absolute;
		left: 0;
		top: 68px;
		background: #111;
	}
	.toggle {
		display: none;
	}
	.submenu-active {
		border-radius: 0;
	}
}

9. Позволяем пользователям закрывать подменю, щелкая в любом месте страницы

Теперь есть только один шаг назад. Поскольку выпадающее меню активируется по событию клика, оно не закрывается автоматически, когда пользователь отводит взгляд от верхнего пункта меню. Это особенно раздражает на настольных компьютерах, где выпадающее меню может закрывать содержимое.

Поэтому было бы неплохо дать пользователям возможность закрывать подменю, щелкнув в любом месте экрана. Мы можем добавить эту возможность с помощью JavaScript:

/* Закрытие подменю из любого места */
function closeSubmenu(e) {
	if (menu.querySelector('.submenu-active')) {
		let isClickInside = menu.querySelector('.submenu-active').contains(e.target);
		if (!isClickInside && menu.querySelector('.submenu-active')) {
			menu.querySelector('.submenu-active').classList.remove('submenu-active');
		}
	}
}
/* Слушатель событий */
document.addEventListener('click', closeSubmenu, false);

Пользовательская функция closeSubmenu() проверяет, есть ли на экране открытое подменю, и если да, то проверяет, кликнул ли пользователь внутри него с помощью свойства target. Если пользователь щелкнул в другом месте экрана, класс .submenu-active будет удален, а подменю закроется само. Мы добавляем слушателя событий к объекту document, так как хотим прослушивать щелчки на всей странице.

Вы создали отзывчивую панель навигации с помощью Flexbox и JavaScript!

Наша отзывчивая панель навигации, ориентированная на мобильные устройства, работает в трех разных макетах.

Вот напоминание о конечном результате:

Flexbox - это отличный инструмент для реализации сложных макетов без каких-либо настроек. Если объединить свойства flexbox по выравниванию, упорядочиванию и размеру с медиа-запросами, можно создавать разные макеты для разных экранов просмотра, не прибегая к манипуляциям с исходным кодом HTML.