как расположить выпадающее меню прямо под верхней панелью

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

Я делаю веб-сайт о поездах и хочу сделать версию для компьютеров и планшетов. В версии для планшетов я хочу скрыть панель навигации и сделать её выпадающим меню, и я это сделал, но не могу разместить его прямо под верхней панелью. Даже если я устанавливаю абсолютное позиционирование и значение top равным высоте верхней панели, это все равно не работает, и при изменении размера экрана положение меню всегда меняется. Например, если я точно подгоняю его для экрана размером в один пиксель, в момент изменения размера для теста оно снова располагается не в том месте.

function TrainDropdown() { 
  const trainMenu = document.getElementById("trains");
  trainMenu.classList.toggle("show");
  console.log("TrainDropdown toggled:", trainMenu.classList.contains("show"));
}

  function Menu() {
    const navBar = document.getElementById("phone-nav-bar");
    navBar.classList.toggle("show-nav-bar");
}
function MobileTrainDropdown() {
  const trainElements = document.getElementById("tablet-trains");
  if (trainElements.style.display === "none") {
      trainElements.style.display = "flex";
  } else {
      trainElements.style.display = "none";
  }
}

window.addEventListener("click", function (event) {
  console.log("Window click detected on:", event.target);

  // Закрыть выпадающее меню поезда, если кликнули вне кнопки выпадающего меню
  if (!event.target.closest('.dropdown-button')) {
    const dropdowns = document.getElementsByClassName("train-elements");
    for (let i = 0; i < dropdowns.length; i++) {
      const openDropdown = dropdowns[i];
      if (openDropdown.classList.contains('show')) {
        openDropdown.classList.remove('show');
        console.log("Closed train dropdown:", openDropdown);
      }
    }
  }

  // Закрыть мобильное меню навигации, если кликнули вне кнопки меню и панели навигации
  const navBar = document.getElementById("phone-nav-bar");
  const menuButton = document.querySelector('.menu-button');
  if (navBar && menuButton && !menuButton.contains(event.target) && !navBar.contains(event.target)) {
    navBar.classList.remove("show-nav-bar");
    console.log("Closed mobile nav menu.");
  }
});
body {
    margin: 0;
}

/*свойства верхней панели*/
.top-bar {
    display: flex;
    align-items: center;
    height: 10vh;
    background-color: #A7C4FF;
}

.branding {
    display: flex;
    align-items: center;
}

.branding img {
    height: 12vh; /* Установить фиксированную высоту для логотипа */
    width: auto; /* Сохранить пропорции */
}

/*свойства переключателя меню*/
.menu-button {
    display: none; /* Скрыто на больших экранах */
}

.mobile-phone-nav-bar {
    position: absolute; /* Позиция под верхней панелью */
    top: 10vh; /* Позиция под верхней панелью */
    left: 0; /* Выровнять по левому краю */
    width: 100%; /* Полная ширина */
    display: none; /* Скрыто по умолчанию */
}

.mobile-phone-branding {
    display: none; /* Скрыто на больших экранах */
}
/*свойства панели навигации*/
.nav-bar {
    display: flex;
    flex: 1;
    align-items: center;
    margin-left: 10vw;
}

/*кнопки*/
.nav-button, .dropdown-button{
    width: 100%;
    height: 8vh;
    line-height: 8vh;
    margin-right: 5px;
    font-family: Unbounded;   
    font-size: 1vw; 
    text-align: center;
    text-decoration: none;
    background-color: #A7C4FF;
    border: none;
    border-radius: 40px;
    transition: background-color 0.4s ease-in-out;
}

.nav-button:hover, .dropdown-button:hover{
    background-color: #8FA9FF;
}

/*свойства выпадающего меню поезда и всех его элементов*/
.train-dropdown{
    position: relative;
    margin-right: 5px;
    width: 100%;
}

.train-elements{
    display: none;
    width: 100%;
    position: absolute;
    top: 10vh;
    background-color: #7f9dfd;
    box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
    border-radius: 0.8vw;
    z-index: 1;
}

.train-elements a{
    padding: 1em;
    color: black;
    text-decoration: none;
}

.train-elements a:hover{
    background-color: #567dff;
}

.train-elements #first-element:hover{
    border-top-left-radius: 0.8vw;
    border-top-right-radius: 0.8vw;
}

.train-elements #last-element:hover{
    border-bottom-left-radius: 0.8vw;
    border-bottom-right-radius: 0.8vw;
}

#mobile-phone-train-button{
    display: none;
}

/*Класс, который JS использует для отображения элементов поезда*/
.show{
    display: flex;
    flex-direction: column;
}

@media only screen and (max-width: 1300px){
    /*скрытие панели навигации для экономии места и превращение её в выпадающее меню*/
    .nav-bar{
        display: none;
    }

    /*кнопка меню, которая заставляет панель навигации появляться*/
    .menu-toggle{
        position: relative;
    }

    .menu-button{
        display: flex;
        height: 8.5vh;
        width: 8.5vh;
        justify-content: center;
        align-items: center;
        margin: 10px;
        border-radius: 20px;
        border: none;
        background-color: #A7C4FF;
    }

    .menu-button img {
        width: 5vw; /* Установить фиксированную ширину */
        height: auto; /* Сохранить пропорции */
        max-width: none; /* Предотвратить изменение размера */
    }

    .menu-button:hover{
        background-color: #8FA9FF;
    }

    /*свойства кнопок*/
    .nav-button, .dropdown-button{
        border-radius: 0;
        background-color: #94b7ff;
    }

    .mobile-phone-nav-bar{
        position: absolute;
        box-shadow: 8px 16px 16px 0px rgba(0,0,0,0.2);
        background-color: #A7C4FF;
        width: 300px;
        top: 10vh;
    }

    /*свойства выпадающего меню поезда*/
    .tablet-train-elements{
        display: none;
        width: 100%;
        flex-direction: column;
        position: absolute;
        background-color: #7f9dfd;
    }

    .tablet-train-elements a{
        padding: 12px 16px;
        color: black;
        text-decoration: none;
    }

    .tablet-train-elements a:hover{
        background-color: #567dff;
    }

    /*скрывает кнопку, которую я буду использовать для телефонного режима, которая
    заменяет кнопку выпадающего меню, так как места недостаточно*/
    #mobile-phone-train-button{
        display: none;
    }

    /*класс, который JS использует для отображения выпадающего меню при нажатии на кнопку*/
    .show-nav-bar{
        display: flex;
        flex-direction: column;
    }
}
<!DOCTYPE html>
<html lang="bg">
<head>

    <!--метаданные-->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!--таблица просмотра-->
    <title>Железопътна страст</title>
    <link rel="icon" href="https://stackoverflow.com/questions/79128757/assets/images/favicon.png">

    <!--CSS файлы-->
    <link rel="stylesheet" href="assets/css/index.css">
    <link rel="stylesheet" href="topbar/topbar.css">

    <!--шрифты Google-->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Unbounded:[email protected]&display=swap" rel="stylesheet">

</head>
<script src="https://stackoverflow.com/questions/79128757/topbar/topbar.js" defer></script>
<body>    
    <!--верхняя панель-->
    <div class="top-bar">
        <div class="mobile-phone-branding">

            <a href="index.html"><img id="logo" src="assets/images/logo.png" alt="Логотип поезда"></a>

        </div>
        <div class="menu-toggle">

            <button class="menu-button" onclick="Menu()"><img src="assets/images/menu.png"></button>

            <div class="mobile-phone-nav-bar" id="phone-nav-bar">

                <a class="nav-button" href="index.html">Начало</a>

                <a class="nav-button" id="mobile-phone-train-button" href="#trains">Влакове</a>

                <div class="tablet-train-dropdown">

                    <button onclick="MobileTrainDropdown()" class="dropdown-button">Влакове</button>

                    <div id="tablet-trains" class="tablet-train-elements">

                        <a href="#">Link 1</a>

                        <a href="#">Link 2</a>

                        <a href="#">Link 3</a>

                    </div>

                </div> 

                <a class="nav-button" href="#contacts">Контакти</a>
                <a class="nav-button" href="#aboutme">Повече за мен</a>

            </div>

        </div>

        <div class="branding">

            <a href="index.html"><img id="logo" src="assets/images/logo.png" alt="Логотип поезда"></a>

        </div>

        <div class="nav-bar">

            <a class="nav-button" href="index.html">Начало</a>

            <div class="train-dropdown">

                <button onclick="TrainDropdown()" class="dropdown-button">Влакове</button>

                <div id="trains" class="train-elements">

                    <a id="first-element" href="#">Link 1</a>

                    <a href="#">Link 2</a>

                    <a id="last-element" href="#">Link 3</a>

                </div>

            </div> 

            <a class="nav-button" href="#contacts">Контакти</a>
            <a class="nav-button" href="#aboutme">Повече за мен</a>

        </div>

    </div>   

</body>
</html>
</div><div class="s-prose js-post-body" itemprop="text">

Проблема заключается в том, что элемент кнопки превышает высоту панели меню.

Вам нужно удалить следующие свойства из вашего CSS:

.menu-button {
    margin: 10px;
}
.menu-toggle {
    position: relative;
}
.mobile-phone-nav-bar {
    position: absolute;
    top: 10vh;
}

Это означает, что ваш код будет выглядеть так:

function TrainDropdown() { 
  const trainMenu = document.getElementById("trains");
  trainMenu.classList.toggle("show");
  console.log("TrainDropdown toggled:", trainMenu.classList.contains("show"));
}

  function Menu() {
    const navBar = document.getElementById("phone-nav-bar");
    navBar.classList.toggle("show-nav-bar");
}
function MobileTrainDropdown() {
  const trainElements = document.getElementById("tablet-trains");
  if (trainElements.style.display === "none") {
      trainElements.style.display = "flex";
  } else {
      trainElements.style.display = "none";
  }
}

window.addEventListener("click", function (event) {
  console.log("Window click detected on:", event.target);

  // Закрыть выпадающее меню поезда, если кликнули вне кнопки выпадающего меню
  if (!event.target.closest('.dropdown-button')) {
    const dropdowns = document.getElementsByClassName("train-elements");
    for (let i = 0; i < dropdowns.length; i++) {
      const openDropdown = dropdowns[i];
      if (openDropdown.classList.contains('show')) {
        openDropdown.classList.remove('show');
        console.log("Closed train dropdown:", openDropdown);
      }
    }
  }

  // Закрыть мобильное меню навигации, если кликнули вне кнопки меню и панели навигации
  const navBar = document.getElementById("phone-nav-bar");
  const menuButton = document.querySelector('.menu-button');
  if (navBar && menuButton && !menuButton.contains(event.target) && !navBar.contains(event.target)) {
    navBar.classList.remove("show-nav-bar");
    console.log("Closed mobile nav menu.");
  }
});
body {
    margin: 0;
}

/*свойства верхней панели*/
.top-bar {
    display: flex;
    align-items: center;
    height: 10vh;
    background-color: #A7C4FF;
}

.branding {
    display: flex;
    align-items: center;
}

.branding img {
    height: 12vh; /* Установить фиксированную высоту для логотипа */
    width: auto; /* Сохранить пропорции */
}

/*свойства переключателя меню*/
.menu-button {
    display: none; /* Скрыто на больших экранах */
}

.mobile-phone-nav-bar {
    position: absolute; /* Позиция под верхней панелью */
    top: 10vh; /* Позиция под верхней панелью */
    left: 0; /* Выровнять по левому краю */
    width: 100%; /* Полная ширина */
    display: none; /* Скрыто по умолчанию */
}

.mobile-phone-branding {
    display: none; /* Скрыто на больших экранах */
}
/*свойства панели навигации*/
.nav-bar {
    display: flex;
    flex: 1;
    align-items: center;
    margin-left: 10vw;
}

/*кнопки*/
.nav-button, .dropdown-button{
    width: 100%;
    height: 8vh;
    line-height: 8vh;
    margin-right: 5px;
    font-family: Unbounded;   
    font-size: 1vw; 
    text-align: center;
    text-decoration: none;
    background-color: #A7C4FF;
    border: none;
    border-radius: 40px;
    transition: background-color 0.4s ease-in-out;
}

.nav-button:hover, .dropdown-button:hover{
    background-color: #8FA9FF;
}

/*свойства выпадающего меню поезда и всех его элементов*/
.train-dropdown{
    position: relative;
    margin-right: 5px;
    width: 100%;
}

.train-elements{
    display: none;
    width: 100%;
    position: absolute;
    top: 10vh;
    background-color: #7f9dfd;
    box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
    border-radius: 0.8vw;
    z-index: 1;
}

.train-elements a{
    padding: 1em;
    color: black;
    text-decoration: none;
}

.train-elements a:hover{
    background-color: #567dff;
}

.train-elements #first-element:hover{
    border-top-left-radius: 0.8vw;
    border-top-right-radius: 0.8vw;
}

.train-elements #last-element:hover{
    border-bottom-left-radius: 0.8vw;
    border-bottom-right-radius: 0.8vw;
}

#mobile-phone-train-button{
    display: none;
}

/*Класс, который JS использует для отображения элементов поезда*/
.show{
    display: flex;
    flex-direction: column;
}

@media only screen and (max-width: 1300px){
    /*скрытие панели навигации для экономии места и превращение её в выпадающее меню*/
    .nav-bar{
        display: none;
    }

    .menu-button{
        display: flex;
        height: 8.5vh;
        width: 8.5vh;
        justify-content: center;
        align-items: center;
        border-radius: 20px;
        border: none;
        background-color: #A7C4FF;
    }

    .menu-button img {
        width: 5vw; /* Установить фиксированную ширину */
        height: auto; /* Сохранить пропорции */
        max-width: none; /* Предотвратить изменение размера */
    }

    .menu-button:hover{
        background-color: #8FA9FF;
    }

    /*свойства кнопок*/
    .nav-button, .dropdown-button{
        border-radius: 0;
        background-color: #94b7ff;
    }

    .mobile-phone-nav-bar{
        box-shadow: 8px 16px 16px 0px rgba(0,0,0,0.2);
        background-color: #A7C4FF;
        width: 300px;
    }

    /*свойства выпадающего меню поезда*/
    .tablet-train-elements{
        display: none;
        width: 100%;
        flex-direction: column;
        position: absolute;
        background-color: #7f9dfd;
    }

    .tablet-train-elements a{
        padding: 12px 16px;
        color: black;
        text-decoration: none;
    }

    .tablet-train-elements a:hover{
        background-color: #567dff;
    }

    /*обеспечивает скрытие кнопки, которую я буду использовать для телефонного режима, 
    которая заменяет кнопку выпадающего меню, так как места недостаточно*/
    #mobile-phone-train-button{
        display: none;
    }

    /*класс, который JS использует для отображения выпадающего меню при нажатии на кнопку*/
    .show-nav-bar{
        display: flex;
        flex-direction: column;
    }
}
<!DOCTYPE html>
<html lang="bg">
<head>

    <!--метаданные-->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!--таблица просмотра-->
    <title>Железопътна страст</title>
    <link rel="icon" href="https://stackoverflow.com/questions/79128757/assets/images/favicon.png">

    <!--CSS файлы-->
    <link rel="stylesheet" href="assets/css/index.css">
    <link rel="stylesheet" href="topbar/topbar.css">

    <!--шрифты Google-->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Unbounded:[email protected]&display=swap" rel="stylesheet">

</head>
<script src="https://stackoverflow.com/questions/79128757/topbar/topbar.js" defer></script>
<body>    
    <!--верхняя панель-->
    <div class="top-bar">
        <div class="mobile-phone-branding">

            <a href="index.html"><img id="logo" src="assets/images/logo.png" alt="Логотип поезда"></a>

        </div>
        <div class="menu-toggle">

            <button class="menu-button" onclick="Menu()"><img src="assets/images/menu.png"></button>

            <div class="mobile-phone-nav-bar" id="phone-nav-bar">

                <a class="nav-button" href="index.html">Начало</a>

                <a class="nav-button" id="mobile-phone-train-button" href="#trains">Влакове</a>

                <div class="tablet-train-dropdown">

                    <button onclick="MobileTrainDropdown()" class="dropdown-button">Влакове</button>

                    <div id="tablet-trains" class="tablet-train-elements">

                        <a href="#">Link 1</a>

                        <a href="#">Link 2</a>

                        <a href="#">Link 3</a>

                    </div>

                </div> 

                <a class="nav-button" href="#contacts">Контакти</a>
                <a class="nav-button" href="#aboutme">Повече за мен</a>

            </div>

        </div>

        <div class="branding">

            <a href="index.html"><img id="logo" src="assets/images/logo.png" alt="Логотип поезда"></a>

        </div>

        <div class="nav-bar">

            <a class="nav-button" href="index.html">Начало</a>

            <div class="train-dropdown">

                <button onclick="TrainDropdown()" class="dropdown-button">Влакове</button>

                <div id="trains" class="train-elements">

                    <a id="first-element" href="#">Link 1</a>

                    <a href="#">Link 2</a>

                    <a id="last-element" href="#">Link 3</a>

                </div>

            </div> 

            <a class="nav-button" href="#contacts">Контакти</a>
            <a class="nav-button" href="#aboutme">Повече за мен</a>

        </div>

    </div>   

</body>
</html>

Ответ или решение

Чтобы правильно расположить выпадающее меню под верхней панелью на адаптивном сайте, который вы разрабатываете для планшетов и настольных компьютеров, важно учитывать несколько ключевых моментов в CSS и JavaScript.

1. Структура HTML

Первая задача заключается в правильной организации структурных элементов. Убедитесь, что ваше меню и кнопка, открывающая выпадающее меню, находятся внутри одного родительского элемента, который будет позиционироваться относительно верхней панели. Ваш HTML-фрагмент для меню может выглядеть следующим образом:

<div class="top-bar">
    ...
    <div class="menu-toggle">
        <button class="menu-button" onclick="Menu()">Меню</button>
        <div class="mobile-phone-nav-bar" id="phone-nav-bar">
            <!-- Здесь будут элементы навигации -->
        </div>
    </div>
</div>

2. Правильное позиционирование в CSS

Как вы уже заметили, использование position: absolute и top: 10vh может привести к проблемам с позиционированием при изменении размера окна. Вместо этого необходимо использовать более адаптивные методы, чтобы учитывать элементы, находящиеся выше выпадающего меню.

Попробуйте обновить ваш CSS следующим образом:

.mobile-phone-nav-bar {
    position: relative; /* Меняем на relative для более стабильного позиционирования */
    display: none; /* Скрываем по умолчанию */
    width: 100%; /* Ширина на 100% */
    background-color: #A7C4FF; /* Цвет фона для видимости */
    box-shadow: 0 2px 5px rgba(0,0,0,0.2); /* Добавление тени для красоты */
}

.show-nav-bar {
    display: block; /* Показываем навигационную панель при активации */
}

Изменения:

  • Позиционирование на relative позволит выпадающим элементам быть привязанными к своим родителям, что уменьшит ошибки в позиционировании.
  • Убедитесь, что высота верхней панели фиксирована (например, 10vh), чтобы правильно вычислить top значение для панели навигации.

3. Обработчик кликов в JavaScript

Ваш JavaScript для показа и скрытия выпадающего меню должен ссылаться на элементы динамически, чтобы минимизировать риск ошибок в позиционировании. Вот как можно улучшить ваш код:

function ToggleMenu() {
    const navBar = document.getElementById("phone-nav-bar");
    navBar.classList.toggle("show-nav-bar");
}

// Закрытие меню при клике вне меню
window.addEventListener("click", function(event) {
    const navBar = document.getElementById("phone-nav-bar");
    if (!navBar.contains(event.target) && !document.querySelector('.menu-button').contains(event.target)) {
        navBar.classList.remove("show-nav-bar");
    }
});

Такой подход к написанию позволит избежать проблем с неверным позиционированием при изменении окна.

4. Тестирование

После внесения всех изменений протестируйте работу меню на разных разрешениях. Обратите внимание на использование медиа-запросов, чтобы управлять отображением элементов в зависимости от ширины экрана.

Заключение

Принятие этих во внимание изменений поможет вам расположить выпадающее меню непосредственно под верхней панелью, сохраняя при этом её адаптивность в различных разрешениях. Важно помнить о тестировании на реальных устройствах, чтобы убедиться в корректности работы вашего интерфейса в соответствии с ожидаемым поведением пользователя.

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

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