Вопрос или проблема
Проблема: в адаптивном режиме кнопка выпадающего списка не перемещается на видимую панель навигации из иконки меню, когда нажимается любой из ее выпадающих пунктов.
В более подробном виде: В адаптивном режиме (ширина экрана менее 730px) панель навигации становится иконкой меню, и я хочу, чтобы любая <a>
или кнопка выпадающего списка <button>
перемещалась на видимую панель навигации, когда нажимается любой пункт выпадающего списка.
Код находится по адресу https://codepen.io/Balazs-Keisz/pen/KKOmgQX
Вот JavaScript, обрабатывающий панель навигации:
// Функция для переключения адаптивного класса для верхней навигации, когда нажата иконка
function myFunctionIcon() {
var x = document.getElementById("myTopnav");
if (x.className === "topnav") {
x.className += " responsive";
} else {
x.className = "topnav";
}
}
// Функция для очистки всех выделений
function clearAllHighlights() {
// Удалить выделение у всех элементов навигации и кнопок
document.querySelectorAll('.nav-item, .dropbtn').forEach(item => {
item.classList.remove('highlight-menu');
});
}
// Функция для выделения нажатой кнопки или ссылки
function highlightButton(clickedElement) {
clearAllHighlights(); // Очистить предыдущие выделения
clickedElement.classList.add('highlight-menu'); // Выделить нажатый элемент
// Если нажатый элемент является частью выпадающего списка, убедитесь, что выделена только кнопка выпадающего списка
const dropdownButton = clickedElement.closest('.dropdown')?.querySelector('.dropbtn');
if (dropdownButton) {
dropdownButton.classList.add('highlight-menu'); // Выделить только кнопку выпадающего списка
}
}
// Обработчики событий для элементов навигации и кнопок выпадающего списка
document.querySelectorAll('.nav-item, .dropbtn').forEach(item => {
item.addEventListener('click', function(event) {
highlightButton(this); // Применить выделение
// Предотвратить закрытие меню при нажатии кнопки выпадающего списка
if (this.classList.contains('dropbtn')) {
event.stopPropagation(); // Предотвратить всплытие события
}
});
});
// Обработчик событий для каждой ссылки выпадающего списка, чтобы закрыть адаптивное меню
document.querySelectorAll('.dropdown-content a').forEach(link => {
link.addEventListener('click', function(event) {
const dropdownButton = this.closest('.dropdown').querySelector('.dropbtn');
highlightButton(dropdownButton); // Выделить только кнопку выпадающего списка
if (window.innerWidth <= 729) {
document.getElementById("myTopnav").classList.remove("responsive"); // Закрыть адаптивное меню
}
});
});
// Обработчик событий для любых других адаптивных <a> ссылок для закрытия меню
document.querySelectorAll('.nav-item').forEach(link => {
link.addEventListener('click', function() {
if (window.innerWidth <= 729) {
document.getElementById("myTopnav").classList.remove("responsive"); // Закрыть адаптивное меню
}
});
});
// Функция переключения выпадающего списка для нескольких выпадающих списков
function toggleDropdown1(dropdownId) {
var dropdown = document.getElementById(dropdownId);
dropdown.classList.toggle("show1");
}
// Функция для закрытия всех выпадающих списков
function closeAllDropdowns() {
var dropdowns = document.getElementsByClassName("dropdown-content");
for (var i = 0; i < dropdowns.length; i++) {
dropdowns[i].classList.remove('show1');
}
}
// Переключение выпадающего списка для нажатой кнопки выпадающего списка
function toggleDropdown1(dropdownContent) {
// Проверить, открыт ли уже нажатый выпадающий список
var isOpen = dropdownContent.classList.contains('show1');
// Закрыть все выпадающие списки
closeAllDropdowns();
// Если нажатый выпадающий список не был открыт, откройте его
if (!isOpen) {
dropdownContent.classList.add('show1');
}
}
// Добавить обработчики событий для каждой кнопки выпадающего списка
document.querySelectorAll('.dropbtn').forEach(button => {
button.addEventListener('click', function(event) {
// Предотвратить закрытие при нажатии на ту же кнопку выпадающего списка
event.stopPropagation();
// Получить соответствующий контент выпадающего списка
var dropdownContent = this.nextElementSibling;
// Переключить выпадающий список
toggleDropdown1(dropdownContent);
});
});
// Закрыть выпадающий список, если пользователь нажимает вне любого выпадающего списка
window.addEventListener('click', function(event) {
if (!event.target.matches('.dropbtn') && !event.target.matches('.dropbtn i')) {
closeAllDropdowns();
}
});
// Привязать обработчики событий ко всем <a> и <button> элементам для выделения
document.querySelectorAll('.nav-item, .dropbtn').forEach(item => {
item.addEventListener('click', function() {
highlightButton(this); // Выделить нажатый элемент
});
});
Проблема заключается в этой части
// Обработчик событий для каждой ссылки контента выпадающего списка, чтобы закрыть адаптивное меню
document.querySelectorAll('.dropdown-content a').forEach(link => {
link.addEventListener('click', function (event) {
const dropdownButton = this.closest('.dropdown').querySelector('.dropbtn');
Вы выбираете неправильный элемент
.querySelector('.dropbtn');
это дочерний элемент выпадающего списка.
Вы не можете показать дочерний элемент, если его родитель скрыт!
Чтобы исправить это, удалите последний .querySelector('.dropbtn')
, и у вас все будет в порядке.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link href="https://fonts.googleapis.com/css?family=Noto Serif" rel="stylesheet">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
html {
font-size: 17px;
/* Базовый размер */
}
body {
font-family: 'Noto Serif';
background-color: #eae6e0;
padding: 0 0 50px 0;
}
/* Добавьте черный цвет фона к верхней навигации */
.topnav {
background-color: #404857;
overflow: hidden;
display: flex;
/* Используйте флексбокс для выравнивания */
justify-content: center;
/* Равномерно распределить кнопки */
}
/* Стиль ссылок внутри навигационной панели */
.topnav a {
float: left;
display: block;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
.topnav .search-container {
float: right;
display: none;
}
.topnav .search-container input[type=text] {
padding: 6px;
margin-top: 8px;
border: none;
}
.topnav .search-container button {
float: right;
padding: 6px 10px;
margin-top: 8px;
margin-right: 16px;
background: #ddd;
border: none;
cursor: pointer;
}
/* Скрыть ссылку, которая должна открывать и закрывать верхнюю навигацию на маленьких экранах */
.topnav .icon {
display: none;
}
/* Контейнер выпадающего списка - необходим для позиционирования контента выпадающего списка */
.dropdown {
float: left;
overflow: hidden;
}
/* Стиль кнопки выпадающего списка, чтобы она вписалась в верхнюю навигацию */
.dropbtn {
border: none;
outline: none;
color: white;
padding: 14px 16px;
background-color: #404857;
font-family: inherit;
font-size: 1rem;
margin: 0;
}
/* Стиль контента выпадающего списка (по умолчанию скрыт) */
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
}
/* Стиль ссылок внутри выпадающего списка */
.dropdown-content a {
float: none;
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
text-align: left;
}
/* Выделить ссылки верхней навигации, кнопку выпадающего списка и контейнер поиска при наведении */
.topnav a:hover,
.dropbtn:hover {
color: #ffa668;
}
/* Добавить серый цвет фона к ссылкам выпадающего списка при наведении */
.dropdown-content a:hover {
background-color: #ddd;
color: #0036cf;
}
/* Выделить текущую страницу */
.highlight-menu {
background-color: #0036cf;
color: white;
}
.dropdown-content.show1 {
display: block;
}
/* Когда ширина экрана менее 730 пикселей, скрыть все ссылки. Показать ссылку, которая должна открывать и закрывать верхнюю навигацию (.icon) */
@media screen and (max-width: 729px) {
.topnav {
display: block;
/* Удалить макет флексбокса */
justify-content: initial;
/* Сбросить содержание выравнивания */
}
.topnav a,
.dropdown,
.dropbtn,
.dropdown-content {
display: none;
/* Скрыть по умолчанию */
}
.highlight-menu {
display: block !important;
/* Убедитесь, что выделенные элементы отображаются */
}
.topnav .dropbtn.highlight-menu {
display: block;
}
.topnav a.icon {
float: left;
display: block;
/* Показать иконку для переключения меню */
}
.show1 {
display: block;
/* Убедитесь, что содержимое выпадающего списка видимо */
}
/* Класс "responsive" добавляется к верхней навигации с помощью JavaScript, когда пользователь нажимает на иконку. Этот класс делает верхнюю навигацию красивой на маленьких экранах (показать ссылки вертикально вместо горизонтально) */
@media screen and (max-width: 729px) {
.topnav.responsive {
position: relative;
}
.topnav.responsive a {
float: none;
display: block;
text-align: left;
}
.topnav.responsive .dropdown,
.dropbtn {
float: none;
display: block;
width: 100%;
text-align: left;
}
.topnav.responsive .dropdown-content {
position: relative;
}
}
}
</style>
<div class="topnav" id="myTopnav">
<!-- ЗАМЕТКА: ДОБАВЛЕН ЭТОТ КОД -->
<div id="tab1"></div>
<div id="tab2"></div>
<div id="tab3"></div>
<div id="tab4"></div>
<div id="tab5"></div>
<!-- ЗАМЕТКА: ДОБАВЛЕН ЭТОТ КОД -->
<!-- Иконка меню (мобильный вид) -->
<a href="javascript:void(0);" class="icon" onclick="myFunctionIcon()">☰</a>
<!-- Элементы навигации (ссылки и кнопки) -->
<div class="dropdown">
<!-- ЗАМЕТКА: УДАЛИТЬ ВЫЗОВ В onclick ', toggleDropdown1('myDropdown2')' -->
<button onclick="highlightButton(this)" class="dropbtn">Введение
<i class="fa fa-caret-down"></i>
</button>
<div id="myDropdown1" class="dropdown-content">
<a href="#" onclick="document.getElementById('tab1').click();">Примечание создателя</a>
<a href="#" onclick="document.getElementById('tab2').click();">Примечание фаната</a>
</div>
</div>
<a href="#cards" id='cards' class="nav-item">Карты</a>
<div class="dropdown">
<!-- ЗАМЕТКА: УДАЛИТЬ ВЫЗОВ В onclick ', toggleDropdown1('myDropdown2')' -->
<button onclick="highlightButton(this)" class="dropbtn">Правила
<i class="fa fa-caret-down"></i>
</button>
<div id="myDropdown2" class="dropdown-content">
<a href="#" onclick="document.getElementById('tab3').click();">Оглавление</a>
<a href="#" onclick="document.getElementById('tab4').click();">Диаграммы</a>
<a href="#" onclick="document.getElementById('tab5').click();">Просмотр правил</a>
</div>
</div>
<a href="#strategies" id='strategies' class="nav-item">Стратегии</a>
<a href="#resources" id='resources' class="nav-item">Ресурсы</a>
<a href="#about" id='about' class="nav-item">О нас</a>
<a href="#contact" id='contact' class="nav-item">Контакт</a>
</div>
</head>
<script>
// Функция для переключения адаптивного класса для верхней навигации, когда нажата иконка
function myFunctionIcon() {
var x = document.getElementById("myTopnav");
if (x.className === "topnav") {
x.className += " responsive";
} else {
x.className = "topnav";
}
}
// Функция для очистки всех выделений
function clearAllHighlights() {
// Удалить выделение у всех элементов навигации и кнопок
document.querySelectorAll('.nav-item, .dropbtn').forEach(item => {
item.classList.remove('highlight-menu');
});
}
// Функция для выделения нажатой кнопки или ссылки
function highlightButton(clickedElement) {
clearAllHighlights(); // Очистить предыдущие выделения
clickedElement.classList.add('highlight-menu'); // Выделить нажатый элемент
// Если нажатый элемент является частью выпадающего списка, убедитесь, что выделена только кнопка выпадающего списка
const dropdownButton = clickedElement.closest('.dropdown')?.querySelector('.dropbtn');
if (dropdownButton) {
dropdownButton.classList.add('highlight-menu'); // Выделить только кнопку выпадающего списка
}
}
// Обработчики событий для элементов навигации и кнопок выпадающего списка
document.querySelectorAll('.nav-item, .dropbtn').forEach(item => {
item.addEventListener('click', function (event) {
highlightButton(this); // Применить выделение
// Предотвратить закрытие меню при нажатии кнопки выпадающего списка
if (this.classList.contains('dropbtn')) {
event.stopPropagation(); // Предотвратить всплытие события
}
});
});
// Обработчик событий для каждой ссылки контента выпадающего списка, чтобы закрыть адаптивное меню
document.querySelectorAll('.dropdown-content a').forEach(link => {
link.addEventListener('click', function (event) {
// ЗАМЕТКА: ВЫБРАН ЭЛЕМЕНТ .dropdown, а не его дочерний
const dropdownButton = this.closest('.dropdown');//.querySelector('.dropbtn');
highlightButton(dropdownButton); // Выделить только кнопку выпадающего списка
if (window.innerWidth <= 729) {
document.getElementById("myTopnav").classList.remove("responsive"); // Закрыть адаптивное меню
}
});
});
// Обработчик событий для любых других адаптивных <a> ссылок для закрытия меню
document.querySelectorAll('.nav-item').forEach(link => {
link.addEventListener('click', function () {
if (window.innerWidth <= 729) {
document.getElementById("myTopnav").classList.remove("responsive"); // Закрыть адаптивное меню
}
});
});
// Функция переключения выпадающего списка для нескольких выпадающих списков
function toggleDropdown1(dropdownId) {
var dropdown = document.getElementById(dropdownId);
dropdown.classList.toggle("show1");
}
// Функция для закрытия всех выпадающих списков
function closeAllDropdowns() {
var dropdowns = document.getElementsByClassName("dropdown-content");
for (var i = 0; i < dropdowns.length; i++) {
dropdowns[i].classList.remove('show1');
}
}
// Переключить выпадающий список для нажатой кнопки выпадающего списка
function toggleDropdown2(dropdownContent) {
// Проверить, открыт ли уже нажатый выпадающий список
var isOpen = dropdownContent.classList.contains('show1');
// Закрыть все выпадающие списки
closeAllDropdowns();
// Если нажатый выпадающий список не был открыт, откройте его
if (!isOpen) {
dropdownContent.classList.add('show1');
}
}
// Добавить обработчики событий для каждой кнопки выпадающего списка
document.querySelectorAll('.dropbtn').forEach(button => {
button.addEventListener('click', function (event) {
// Предотвратить закрытие при нажатии на ту же кнопку выпадающего списка
event.stopPropagation();
// Получить соответствующий контент выпадающего списка
var dropdownContent = this.nextElementSibling;
// Переключить выпадающий список
toggleDropdown2(dropdownContent);
});
});
// Закрыть выпадающий список, если пользователь нажимает вне любого выпадающего списка
window.addEventListener('click', function (event) {
if (!event.target.matches('.dropbtn') && !event.target.matches('.dropbtn i')) {
closeAllDropdowns();
}
});
// Привязать обработчики событий ко всем <a> и <button> элементам для выделения
document.querySelectorAll('.nav-item, .dropbtn').forEach(item => {
item.addEventListener('click', function () {
highlightButton(this); // Выделить нажатый элемент
});
});
</script>
</body>
</html>
Ответ или решение
Создание адаптивной навигационной панели с выпадающими кнопками
В современном веб-дизайне создание адаптивной навигационной панели—это основа пользовательского опыта (UX). Основная задача заключается в том, чтобы гарантировать, что все элементы интерфейса работают должным образом на различных устройствах и в разных разрешениях экрана. В данной статье мы разберемся с проблемой, связанной с адаптивной навигационной панелью с выпадающими кнопками, в которой выпадающие элементы не исчезают и не отображаются корректно при взаимодействии с ними.
Проблема
При работе с адаптивной навигационной панелью на устройствах с шириной экрана менее 730 пикселей, кнопки с выпадающими меню должны перемещаться в видимую область навигационной панели после нажатия на любой из пунктов выпадающего меню. На данный момент при нажатии на элементы выпадающих меню кнопки не возвращаются в видимую панель.
Код и Его Оптимизация
В представленном коде JavaScript, который управляет навигационной панелью, мы видим последовательность действий, которые выполняются пользователем для активации различных элементов интерфейса. Основная логика реализована с использованием функций для обработки событий, однако в коде имеется ошибка, связанная с неправильным выбором элемента.
document.querySelectorAll('.dropdown-content a').forEach(link => {
link.addEventListener('click', function (event) {
const dropdownButton = this.closest('.dropdown');//.querySelector('.dropbtn');
Решение
Как указано в заметках, при выборе элемента выпадающего меню (.dropdown-content
), необходимо убрать часть, которая выбирает дочерний элемент .dropbtn
. Вместо этого, следует выбрать сам элемент dropdown
, так как при скрытом родителе к нему нельзя получить доступ из его дочерних элементов.
Оптимизированный фрагмент кода:
document.querySelectorAll('.dropdown-content a').forEach(link => {
link.addEventListener('click', function (event) {
const dropdown = this.closest('.dropdown');
highlightButton(dropdown); // Выделить только кнопку выпадающего меню
if (window.innerWidth <= 729) {
document.getElementById("myTopnav").classList.remove("responsive"); // Закрыть адаптивное меню
}
});
});
Применение стилей
Также стоит уделить внимание CSS для обеспечения гибкости интерфейса. Вот базовые стили, которые могут быть применены:
@media screen and (max-width: 729px) {
.topnav {
display: block; /* Уберите flexbox для мобильных устройств */
}
.topnav a, .dropdown, .dropbtn, .dropdown-content {
display: none; /* Скрыть элементы по умолчанию */
}
.highlight-menu {
display: block !important; /* Убедитесь, что выделенные элементы отображаются */
}
.topnav .dropbtn.highlight-menu {
display: block;
}
.show1 {
display: block; /* Убедитесь, что содержимое выпадающего меню отображается */
}
}
Заключение
Оптимизация адаптивных навигационных панелей требует высокой степени внимания к деталям. Убедитесь, что все элементы правильно выделяются и управляются в зависимости от взаимодействия пользователь. Тестируйте навигацию на разных устройствах, чтобы гарантировать, что все работает безупречно. Правильное использование JavaScript и CSS для этих задач обеспечит удобный и интуитивно понятный интерфейс для пользователей.
С приступив к решению, примените предложенные изменения и протестируйте поведение навигационной панели. Успех в этом проекте положительно скажется на вашем сайте, улучшая пользовательский опыт.