Адаптивная навигационная панель с выпадающими кнопками

Вопрос или проблема

Проблема: в адаптивном режиме кнопка выпадающего списка не перемещается на видимую панель навигации из иконки меню, когда нажимается любой из ее выпадающих пунктов.

В более подробном виде: В адаптивном режиме (ширина экрана менее 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()">&#9776;</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 для этих задач обеспечит удобный и интуитивно понятный интерфейс для пользователей.

С приступив к решению, примените предложенные изменения и протестируйте поведение навигационной панели. Успех в этом проекте положительно скажется на вашем сайте, улучшая пользовательский опыт.

Оцените материал
Добавить комментарий

Капча загружается...