Для створення мобільного меню у 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 залишається невеликим, зрозумілим і без сторонніх залежностей.
en