Как отследить клик вне элемента
Короткий ответ
Слушайте клики по всему документу и проверяйте через contains(), был ли клик внутри элемента:
<button id="toggle">Menu</button>
<ul id="menu" hidden>
<li>Profile</li>
<li>Settings</li>
</ul>
<script>
const menu = document.getElementById('menu');
const toggle = document.getElementById('toggle');
toggle.addEventListener('click', () => {
menu.hidden = !menu.hidden;
});
document.addEventListener('click', (event) => {
if (!menu.contains(event.target) && !toggle.contains(event.target)) {
menu.hidden = true;
}
});
</script>Через closest
closest() находит ближайшего родителя, подходящего под селектор. Это удобно, когда выпадающих меню несколько:
document.addEventListener('click', (event) => {
if (!event.target.closest('.dropdown')) {
document.querySelectorAll('.dropdown.open').forEach((el) => el.classList.remove('open'));
}
});Клавиша Escape
Пользователи ожидают, что окно закроется и по Escape:
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') menu.hidden = true;
});Частая ошибка
Без проверки кнопки клик по ней открывает меню, а затем тот же клик доходит до документа и сразу его закрывает. Исключите кнопку или остановите событие через event.stopPropagation().
toggle.addEventListener('click', (event) => {
event.stopPropagation();
menu.hidden = !menu.hidden;
});