Как отследить клик вне элемента
Хорошая практика для удобства пользователя реализовать закрытие динамического элемента не только при клике на кнопку, а также при клике вне этого элемента
В примере ниже, при нажатии на кнопку Nav отображается блок навигации. Его можно закрыть при клике на кнопку Nav или при клике в любом месте за пределами блока навигации
HTML
Структура HTML данного примера выглядит следующим образом
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title></title>
<link rel="stylesheet" href="css/bootstrap-reboot.min.css"/>
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<div class="header">
<div class="container">
<div class="header__flex">
<div class="header__logo">Logo</div>
<div class="header__button">Nav</div>
</div>
<div class="header__nav nav">
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Portfolio</a></li>
<li><a href="#">Contact</a></li>
</ul>
</div>
</div>
</div>
<script src="js/main.js"></script>
</body>
</html>
CSS
Напишем стили
Для плавного появления и исчезания окна навигации используем комбинацию свойств transition
, opacity
и visibility
Чтобы при закрытии окно навигации скрывалось не мгновенно для свойства visibility
применяем задержку 0.6s transition: visibility 0s 0.6s, opacity 0.6s ease;
, то есть окно навигации скрывается полностью только после того, как отработает плавное исчезание через свойство opacity
.container {
max-width: 1140px;
margin: 0 auto;
padding: 0 15px;
}
.header {
padding: 15px 0;
background: #485696;
border-radius: 15px;
}
.header .container {
position: relative;
}
.header__flex {
display: flex;
align-items: center;
justify-content: space-between;
}
.header__logo {
font-size: 24px;
font-weight: bold;
color: #fff;
}
.header__button {
background: #fff;
height: 48px;
border-radius: 15px;
padding: 0 48px;
display: inline-flex;
align-items: center;
justify-content: center;
color: #485696;
position: relative;
font-size: 18px;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
transition: 0.3s ease;
border: 2px solid transparent;
}
.header__button:hover {
background: #485696;
color: #fff;
border-color: #fff;
}
.nav {
position: absolute;
top: calc(100% + 30px);
right: 15px;
background: #485696;
border-radius: 25px;
font-size: 18px;
padding: 15px 0;
visibility: hidden;
transition: visibility 0s 0.6s, opacity 0.6s ease; /* при закрытии окна навигации, сначала плавно понижается непрозрачность - opacity: 0; затем мгновенно исчезает через 0.6s ожидания - visibility: hidden; */
opacity: 0;
overflow: hidden;
}
.nav_active {
transition: visibility 0s 0s, opacity 0.6s ease; /* при открытии окна навигации, оно сначала мгновенно появляется - visibility: visible;, затем плавно повышается непрозрачность - opacity: 1; */
opacity: 1;
visibility: visible;
}
.nav ul {
padding: 0;
margin: 0;
list-style: none;
}
.nav ul li a {
display: block;
padding: 5px 48px;
white-space: nowrap;
color: #fff;
text-decoration: none;
transition: 0.3s ease;
}
.nav ul li a:hover {
background: #6372b4;
}
.nav ul li a:active {
background: #8a95c7;
}
JavaScript
Напишем логику на JavaScript
e.target
- элемент, на котором произошло определенное событие, в данном случае - клик
closest()
- этот метод ищет ближайший родительский элемент по заданному селектору
Ниже код с комментариями
document.addEventListener('DOMContentLoaded', () => { // Структура страницы загружена и готова к взаимодействию
const button = document.querySelector('.header__button') // находим кнопку для открытия/закрытия окна навигации
const nav = document.querySelector('.nav') // находим окно навигации
button.addEventListener('click', () => { // при клике на кнопку
nav.classList.toggle('nav_active') // открываем/закрываем окно навигации, добаляя/удаляя активный класс
})
window.addEventListener('click', e => { // при клике в любом месте окна браузера
const target = e.target // находим элемент, на котором был клик
if (!target.closest('.nav') && !target.closest('.header__button')) { // если этот элемент или его родительские элементы не окно навигации и не кнопка
nav.classList.remove('nav_active') // то закрываем окно навигации, удаляя активный класс
}
})
})