Як створити мобільне меню з burger-кнопкою без jQuery

Для створення мобільного меню у WordPress зовсім не обов’язково підключати jQuery або сторонні JavaScript-бібліотеки.

Сучасного Vanilla JavaScript цілком достатньо, щоб реалізувати:

  • відкриття та закриття меню;
  • анімацію burger-кнопки;
  • блокування прокрутки сторінки;
  • закриття меню клавішею Escape;
  • закриття після переходу за посиланням;
  • коректні aria-* атрибути для accessibility.

У цій статті створимо просте мобільне меню для власної WordPress-теми.

1. Виводимо меню в header.php

Припустимо, що у functions.php вже зареєстрована позиція меню:

function wp_hunter_setup() {

	register_nav_menus(
		array(
			'primary' => __( 'Primary Menu', 'wp-hunter' ),
		)
	);

}
add_action( 'after_setup_theme', 'wp_hunter_setup' );

Тепер у header.php перевіримо, чи призначене меню, та додамо burger-кнопку:

<?php if ( has_nav_menu( 'primary' ) ) : ?>

	<button
		class="menu-toggle"
		type="button"
		aria-expanded="false"
		aria-controls="primary-navigation"
		aria-label="<?php esc_attr_e( 'Open menu', 'wp-hunter' ); ?>"
	>
		<span class="menu-toggle__line"></span>
		<span class="menu-toggle__line"></span>
		<span class="menu-toggle__line"></span>
	</button>

	<nav
		id="primary-navigation"
		class="primary-navigation"
		aria-label="<?php esc_attr_e( 'Primary Navigation', 'wp-hunter' ); ?>"
	>
		<?php
		wp_nav_menu(
			array(
				'theme_location' => 'primary',
				'container'      => false,
				'menu_class'     => 'primary-menu',
				'fallback_cb'    => false,
			)
		);
		?>
	</nav>

<?php endif; ?>

Тут важливими є два атрибути:

aria-expanded="false"

показує, чи відкрите меню.

А:

aria-controls="primary-navigation"

вказує, яким елементом керує кнопка.

ID:

id="primary-navigation"

відповідно встановлений для <nav>.

2. Створюємо burger-кнопку

Всередині кнопки ми використали три звичайні <span>:

<span class="menu-toggle__line"></span>
<span class="menu-toggle__line"></span>
<span class="menu-toggle__line"></span>

Саме з них за допомогою CSS створимо burger.

.menu-toggle {
	display: none;
	width: 44px;
	height: 44px;
	padding: 10px;
	border: 0;
	background: transparent;
	cursor: pointer;
}

.menu-toggle__line {
	display: block;
	width: 24px;
	height: 2px;
	margin: 5px auto;
	background: currentColor;
	transition:
		transform 0.3s ease,
		opacity 0.3s ease;
}

На desktop кнопка прихована:

display: none;

Показувати її будемо лише на мобільних пристроях.

3. Анімація burger → close

Коли меню відкрите, JavaScript змінюватиме:

aria-expanded="false"

на:

aria-expanded="true"

Це дозволяє керувати анімацією без додаткових CSS-класів:

.menu-toggle[aria-expanded="true"] .menu-toggle__line:nth-child(1) {
	transform: translateY(7px) rotate(45deg);
}

.menu-toggle[aria-expanded="true"] .menu-toggle__line:nth-child(2) {
	opacity: 0;
}

.menu-toggle[aria-expanded="true"] .menu-toggle__line:nth-child(3) {
	transform: translateY(-7px) rotate(-45deg);
}

Три лінії burger-кнопки плавно перетворяться на хрестик.

4. Стилі мобільного меню

Тепер створимо саме мобільне меню.

Наприклад, нехай воно виїжджає справа:

@media (max-width: 991px) {

	.menu-toggle {
		display: block;
		position: relative;
		z-index: 1001;
	}

	.primary-navigation {
		position: fixed;
		top: 0;
		right: 0;
		bottom: 0;
		width: min(400px, 100%);
		padding: 100px 30px 40px;
		background: #fff;

		transform: translateX(100%);
		visibility: hidden;
		transition:
			transform 0.35s ease,
			visibility 0.35s;
		z-index: 1000;
	}

	body.menu-open .primary-navigation {
		transform: translateX(0);
		visibility: visible;
	}

	.primary-menu {
		display: flex;
		flex-direction: column;
		gap: 20px;
		margin: 0;
		padding: 0;
		list-style: none;
	}

	.primary-menu a {
		display: block;
		text-decoration: none;
	}

	body.menu-open {
		overflow: hidden;
	}
}

Ключовим тут є клас:

body.menu-open

Коли він з’являється на <body>, меню стає видимим.

Також:

body.menu-open {
	overflow: hidden;
}

блокує прокрутку сторінки під відкритим мобільним меню.

5. JavaScript без jQuery

Створимо файл:

/assets/js/navigation.js

і додамо:

document.addEventListener('DOMContentLoaded', () => {

	const toggle = document.querySelector('.menu-toggle');
	const navigation = document.querySelector('.primary-navigation');

	if (!toggle || !navigation) {
		return;
	}

	const openMenu = () => {
		document.body.classList.add('menu-open');

		toggle.setAttribute('aria-expanded', 'true');
		toggle.setAttribute('aria-label', 'Close menu');
	};

	const closeMenu = () => {
		document.body.classList.remove('menu-open');

		toggle.setAttribute('aria-expanded', 'false');
		toggle.setAttribute('aria-label', 'Open menu');
	};

	const toggleMenu = () => {
		const isOpen = toggle.getAttribute('aria-expanded') === 'true';

		if (isOpen) {
			closeMenu();
		} else {
			openMenu();
		}
	};

	toggle.addEventListener('click', toggleMenu);

});

Все.

Для базового мобільного меню нам не потрібні ні jQuery, ні Bootstrap, ні будь-які інші залежності.

6. Закриваємо меню клавішею Escape

Додамо можливість закрити меню клавішею Escape.

Це особливо важливо для користувачів, які працюють із сайтом за допомогою клавіатури.

document.addEventListener('keydown', (event) => {

	if (event.key === 'Escape') {
		closeMenu();
	}

});

Тепер натискання:

Esc

закриє мобільне меню.

7. Закриваємо меню після кліку на посилання

На мобільних пристроях після вибору пункту навігації меню також бажано закривати.

Додаємо:

navigation.addEventListener('click', (event) => {

	if (event.target.closest('a')) {
		closeMenu();
	}

});

Це особливо корисно, якщо меню містить anchor-посилання:

#about
#services
#contacts

Користувач натискає пункт — меню закривається, а браузер переходить до потрібної секції.

8. Закриваємо меню при кліку поза ним

Також можна закривати меню, якщо користувач натиснув у будь-якому місці за межами навігації.

document.addEventListener('click', (event) => {

	const clickedInsideMenu = navigation.contains(event.target);
	const clickedToggle = toggle.contains(event.target);

	if (!clickedInsideMenu && !clickedToggle) {
		closeMenu();
	}

});

При цьому клік на самому меню або burger-кнопці не викликатиме закриття.

9. Враховуємо зміну розміру браузера

Є ще один невеликий нюанс.

Наприклад, користувач відкрив мобільне меню, а потім збільшив ширину браузера до desktop.

На <body> все ще залишиться:

class="menu-open"

Тому краще автоматично скинути стан меню при переході на desktop.

const desktopMedia = window.matchMedia('(min-width: 992px)');

desktopMedia.addEventListener('change', (event) => {

	if (event.matches) {
		closeMenu();
	}

});

Тепер при переході вище 992px мобільне меню автоматично повернеться у початковий стан.

10. Повний JavaScript

У результаті наш navigation.js виглядатиме так:

document.addEventListener('DOMContentLoaded', () => {

	const toggle = document.querySelector('.menu-toggle');
	const navigation = document.querySelector('.primary-navigation');

	if (!toggle || !navigation) {
		return;
	}

	const openMenu = () => {
		document.body.classList.add('menu-open');

		toggle.setAttribute('aria-expanded', 'true');
		toggle.setAttribute('aria-label', 'Close menu');
	};

	const closeMenu = () => {
		document.body.classList.remove('menu-open');

		toggle.setAttribute('aria-expanded', 'false');
		toggle.setAttribute('aria-label', 'Open menu');
	};

	const toggleMenu = () => {
		const isOpen = toggle.getAttribute('aria-expanded') === 'true';

		isOpen ? closeMenu() : openMenu();
	};

	toggle.addEventListener('click', toggleMenu);

	document.addEventListener('keydown', (event) => {

		if (event.key === 'Escape') {
			closeMenu();
		}

	});

	navigation.addEventListener('click', (event) => {

		if (event.target.closest('a')) {
			closeMenu();
		}

	});

	document.addEventListener('click', (event) => {

		const clickedInsideMenu = navigation.contains(event.target);
		const clickedToggle = toggle.contains(event.target);

		if (!clickedInsideMenu && !clickedToggle) {
			closeMenu();
		}

	});

	const desktopMedia = window.matchMedia('(min-width: 992px)');

	desktopMedia.addEventListener('change', (event) => {

		if (event.matches) {
			closeMenu();
		}

	});

});

Для звичайного мобільного меню цього більш ніж достатньо.

11. Підключаємо JavaScript у WordPress

Не варто вставляти:

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

безпосередньо в header.php або footer.php.

WordPress має власний механізм підключення JavaScript-файлів — wp_enqueue_script().

Додаємо у functions.php:

function wp_hunter_scripts() {

	wp_enqueue_script(
		'wp-hunter-navigation',
		get_template_directory_uri() . '/assets/js/navigation.js',
		array(),
		wp_get_theme()->get( 'Version' ),
		array(
			'strategy'  => 'defer',
			'in_footer' => true,
		)
	);

}
add_action( 'wp_enqueue_scripts', 'wp_hunter_scripts' );

Наш скрипт не використовує jQuery, тому список залежностей порожній:

array()

Тобто WordPress не буде завантажувати jQuery заради роботи мобільного меню.

12. Повна структура

У результаті структура теми може виглядати так:

your-theme/
│
├── assets/
│   ├── css/
│   │   └── main.css
│   │
│   └── js/
│       └── navigation.js
│
├── functions.php
├── header.php
├── footer.php
├── index.php
└── style.css

У functions.php:

register_nav_menus();

реєструємо позицію меню.

У header.php:

wp_nav_menu();

виводимо навігацію та burger-кнопку.

У:

navigation.js

керуємо відкриттям та закриттям меню.

А CSS відповідає за зовнішній вигляд та анімацію.

Чому краще без jQuery?

Колись для такої задачі часто використовували:

$('.menu-toggle').on('click', function() {
	$('.menu').toggleClass('active');
});

Але сьогодні для настільки простої логіки jQuery не потрібен.

Vanilla JavaScript вже має все необхідне:

document.querySelector()
addEventListener()
classList.add()
classList.remove()
classList.toggle()
closest()
matchMedia()

У результаті ми не створюємо зайву залежність і залишаємо код теми простішим.

Не забуваємо про accessibility

Burger-кнопка повинна бути саме:

<button>

а не:

<div>

або:

<span>

Кнопка вже має правильну семантику та доступна для керування клавіатурою.

Також використовуємо:

aria-expanded="false"

щоб повідомляти стан меню.

Коли меню відкривається:

aria-expanded="true"

А через:

aria-controls="primary-navigation"

зв’язуємо кнопку безпосередньо з навігацією.

Підсумок

Для створення сучасного мобільного меню у WordPress не потрібен jQuery.

Достатньо:

wp_nav_menu()
+
CSS
+
Vanilla JavaScript

У нашому прикладі burger-кнопка:

  • відкриває та закриває меню;
  • анімується у хрестик;
  • змінює aria-expanded;
  • блокує прокрутку сторінки;
  • закривається через Escape;
  • закривається після переходу за посиланням;
  • закривається при кліку поза меню;
  • автоматично скидається при переході на desktop.

Для більшості власних WordPress-тем цього достатньо, а JavaScript залишається невеликим, зрозумілим і без сторонніх залежностей.

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *

WP-Hunter

Безпечний вхід до акаунта

Увійдіть, створіть акаунт або відновіть пароль.

З поверненням

Увійдіть, щоб продовжити.