Модальне вікно та dropdown меню
У цьому завданні ти створиш два популярні UI-компоненти, які зустрічаються на кожному сайті. Працюватимеш з DOM-маніпуляціями, обробкою подій та CSS-класами для анімацій.
Файлова структура
modal-dropdown/
├── index.html
├── style.css
└── script.js
Частина 1: Модальне вікно (Modal)
Створи кнопку, яка відкриває модальне вікно з напівпрозорим фоном (overlay).
Вимоги
1. HTML-структура:
<button class="btn-open-modal">Відкрити модалку</button>
<div class="modal-overlay hidden">
<div class="modal">
<button class="modal-close">×</button>
<h2>Заголовок модалки</h2>
<p>Тут може бути будь-який контент...</p>
</div>
</div>
2. Відкриття модалки:
- Клік на кнопку
.btn-open-modal— прибирає класhiddenз.modal-overlay - При відкритті додай клас
no-scrollдоdocument.body, щоб заблокувати прокрутку фону
// Приблизна логіка
openBtn.addEventListener('click', () => {
overlay.classList.remove('hidden');
document.body.classList.add('no-scroll');
});
3. Закриття модалки (три способи):
- Клік на кнопку закриття (
×) - Клік на overlay (темний фон за модалкою)
- Натискання клавіші
Escape
// Закриття по Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeModal();
}
});
// Клік на overlay (але НЕ на саму модалку)
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
closeModal();
}
});
4. Анімація появи:
- Overlay: плавна поява фону через
opacityтаtransition - Модалка: поява з ефектом
transform: scale(0.8)->scale(1)абоtranslateY(-20px)->translateY(0) - Використай CSS-класи та
transition, не JS-анімацію
/* Приклад CSS */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
opacity: 1;
transition: opacity 0.3s ease;
}
.modal-overlay.hidden {
opacity: 0;
pointer-events: none;
}
.modal {
background: white;
padding: 2rem;
border-radius: 12px;
max-width: 500px;
width: 90%;
transform: scale(1);
transition: transform 0.3s ease;
}
.modal-overlay.hidden .modal {
transform: scale(0.8);
}
.no-scroll {
overflow: hidden;
}
Частина 2: Dropdown меню
Створи кілька dropdown-кнопок, кожна з яких розкриває власне меню.
Вимоги
1. HTML-структура (3 dropdown):
<div class="dropdown">
<button class="dropdown-toggle">Файл ▾</button>
<ul class="dropdown-menu">
<li><a href="#">Новий</a></li>
<li><a href="#">Відкрити</a></li>
<li><a href="#">Зберегти</a></li>
</ul>
</div>
<div class="dropdown">
<button class="dropdown-toggle">Редагувати ▾</button>
<ul class="dropdown-menu">
<li><a href="#">Копіювати</a></li>
<li><a href="#">Вставити</a></li>
<li><a href="#">Вирізати</a></li>
</ul>
</div>
<!-- Ще один dropdown -->
2. Відкриття/закриття:
- Клік на кнопку
.dropdown-toggle— toggle класуopenна батьківському.dropdown - Якщо один dropdown вже відкритий, при відкритті іншого попередній закривається
// Закрити всі dropdown, крім поточного
function closeAllDropdowns(except) {
document.querySelectorAll('.dropdown').forEach(dd => {
if (dd !== except) {
dd.classList.remove('open');
}
});
}
3. Закриття при кліку зовні:
document.addEventListener('click', (e) => {
if (!e.target.closest('.dropdown')) {
closeAllDropdowns();
}
});
4. Клавіатурна навігація:
EnterабоSpaceна кнопці — відкрити/закрити dropdownEscape— закрити відкритий dropdown
5. CSS для dropdown:
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
background: white;
border: 1px solid #ddd;
border-radius: 8px;
list-style: none;
padding: 0.5rem 0;
min-width: 160px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
opacity: 0;
transform: translateY(-10px);
pointer-events: none;
transition: opacity 0.2s, transform 0.2s;
}
.dropdown.open .dropdown-menu {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
Частина 3 (бонус): Confirm-діалог
Створи варіант модалки — діалог підтвердження з двома кнопками.
function confirmDialog(message) {
return new Promise((resolve) => {
// Створити модалку з текстом message
// Кнопки "Так" та "Ні"
// "Так" -> resolve(true)
// "Ні" -> resolve(false)
});
}
// Використання:
const result = await confirmDialog('Ти впевнений?');
if (result) {
console.log('Користувач підтвердив');
} else {
console.log('Користувач скасував');
}
Вимоги:
- Функція повертає
Promise - Модалка створюється динамічно через
createElement - Після натискання кнопки модалка видаляється з DOM
- Той самий стиль та анімації, що й у основній модалці
Підказки
e.target === overlayперевіряє, що клік був саме на overlay, а не на дочірній елементe.target.closest('.dropdown')знаходить найближчий батьківський.dropdown(абоnull, якщо кліку не на dropdown)pointer-events: noneробить елемент "невидимим" для кліків, навіть якщо він видимий на екрані- Для блокування прокрутки:
overflow: hiddenнаbody transitionпрацює тільки між конкретними значеннями (не зdisplay: none)
Критерії оцінки
| Критерій | Бали |
|---|---|
| Модалка відкривається/закривається (кнопка, overlay, Escape) | 25 |
| Блокування прокрутки та CSS-анімація модалки | 15 |
| Dropdown toggle (відкриття/закриття по кліку) | 20 |
| Тільки один dropdown відкритий одночасно | 10 |
| Закриття dropdown при кліку зовні | 15 |
| Клавіатурна навігація (Enter, Escape) | 15 |
| Бонус: Confirm-діалог з Promise | +15 |