Search code examples
javascripthtmlcssnavbar

Sticky responsive top navigation bar with 3-line dropdown menu


I have a simple sticky responsive top navigation bar with 3-line dropdown menu.

<html>

<head>
    <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">
    <style>
        body {
            margin: 0;
            font-family: inherit
        }

        .header {
            font-family: Consolas, Helvetica, sans-serif, Arial;
            padding: 20px;
            text-align: center;
            background-image: linear-gradient(to right, #000a98, #00B8FF);
            color: white;
            font-size: 15px;

        }

        .topnav {
            overflow: hidden;
            background-color: #333;
            background-image: linear-gradient(to right, #000a98, #00B8FF);
        }


        .topnav a {
            float: left;
            display: block;
            color: #f2f2f2;
            text-align: center;
            padding: 14px 16px;
            text-decoration: none;
            font-size: 17px;
        }

        .active {
            background-image: linear-gradient(to right, #000a98, #00B8FF);
            color: white;
        }

        .topnav .icon {
            display: none;
        }

        .dropdown {
            float: left;
            overflow: hidden;

        }

        .dropdown .dropbtn {
            font-size: 17px;
            border: none;
            outline: none;
            color: white;
            padding: 14px 16px;
            background-color: inherit;
            font-family: inherit;
            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,
        .dropdown:hover .dropbtn {
            background-color: #555;
            color: white;
        }

        .dropdown-content a:hover {
            background-color: #ddd;
            color: black;
        }

        .dropdown:hover .dropdown-content {
            display: block;
        }

        @media screen and (max-width: 600px) {

            .topnav a:not(:first-child),
            .dropdown .dropbtn {
                display: none;
            }

            .topnav a.icon {
                float: right;
                display: block;
            }
        }

        @media screen and (max-width: 600px) {
            .topnav.responsive {
                position: relative;
            }

            .topnav.responsive .icon {
                position: absolute;
                right: 0;
                top: 0;
            }

            .topnav.responsive a {
                float: none;
                display: block;
                text-align: left;
            }

            .topnav.responsive .dropdown {
                float: none;
            }

            .topnav.responsive .dropdown-content {
                position: relative;
            }

            .topnav.responsive .dropdown .dropbtn {
                display: block;
                width: 100%;
                text-align: left;
            }

        }

        .content {
            padding: 16px;
        }

        .sticky {
            position: fixed;
            top: 0;
            width: 100%;
        }

        .sticky+.content {
            padding-top: 60px;
        }
    </style>
</head>

<body>
    <div class="header">hhvvvvvvvvv
    </div>
    <div class="topnav" id="myTopnav">
        <a href="#home" class="active">Home</a>
        <a href="#news">News</a>
        <a href="#contact">Contact</a>
        <div class="dropdown">

            <button class="dropbtn">Dropdown
                <i class="fa fa-caret-down"></i>
            </button>
            <div class="dropdown-content">
                <a href="#">Link 1</a>
                <a href="#">Link 2</a>
                <a href="#">Link 3</a>
            </div>
        </div>
        <a href="#about">About</a>
        <a href="javascript:void(0);" style="font-size:15px;" class="icon" onclick="myFunction()">&#9776;</a>

    </div>
    <script>
        function myFunction() {
            var x = document.getElementById("myTopnav");

            if (x.className === "topnav") {
                x.className += " responsive";
            } else {
                x.className = "topnav";
            }
        }
        window.onscroll = function () { myFunction() };

        var navbar = document.getElementById("myTopnav");
        var sticky = navbar.offsetTop;

        function myFunction() {
            if (window.pageYOffset >= sticky) {
                navbar.classList.add("sticky")
            } else {
                navbar.classList.remove("sticky");
            }
        }

        window.addEventListener('scroll', stickyNavigation);

    </script>
    <div style="padding-left:16px">
        <h2>Responsive Topnav with Dropdown</h2>

        <p>Resize the browser window to see how it works.</p>
        <p>Hover over the dropdown button to open the dropdown menu.</p>
        <h2>Responsive Topnav with Dropdown</h2>
        <p>Resize the browser window to see how it works.</p>
        <p>Hover over the dropdown button to open the dropdown menu.</p>
        <h2>Responsive Topnav with Dropdown</h2>
        <p>Resize the browser window to see how it works.</p>
        <p>Hover over the dropdown button to open the dropdown menu.</p>
        <h2>Responsive Topnav with Dropdown</h2>
        <p>Resize the browser window to see how it works.</p>
        <p>Hover over the dropdown button to open the dropdown menu.</p>

</body>
</html>

To see the live demo of above code click here Everything is fine but dropdown is not working. Before I tried to make navbar sticky, dropdown is working perfect but I am facing this problem after making navbar sticky.


Solution

  • Building off the previous version of the answer, I found the reason why clicking on the 3 line menu dropdown (called a hamburger actually!) was not displaying when we were not at the top of the page.

    This was because in your media query, you were setting the .topnav.responsive class to be position: relative, which opened up your menu at the top of the page, rather than where we were expecting to.

    @media screen and (max-width: 600px) {
        .topnav.responsive {
            position: relative; <--- This was causing the issue
    }
    

    To fix this, all you need to do is comment out the .topnav.responsive class here.

    In addition, in the script provided, it does not account for the added sticky CSS class we have added. The example you were building off of was only using the responsive class dynamically and wasn't checking for the dynamic sticky class responsible for keeping the nav bar in the display of the user. To fix that, we will need to edit the logic of the script.

            function myFunction() {
                var x = document.getElementById("myTopnav");
    
                if (x.className === "topnav") { // this is only checking for responsiveness and not checking when the topnav has the `sticky` class
                    x.className += " responsive";
                } else {
                    x.className = "topnav";
                }
            }
    

    To fix this, I updated the logic to only check for if the topnav has the responsive class attached to it.

    function reactiveTopNav() {
        const topNav = document.getElementById('myTopnav');
        
        // checks if the topnav already has the class 'responsive'
        // if it does, remove it, else, add it in
        // this only checks for `responsive` and not any other class
        topNav.classList.contains('responsive')
            ? topNav.classList.remove('responsive')
            : topNav.classList.add('responsive');
    }
    

    Putting it together, this is what I had

    <html>
    
    <head>
      <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" />
      <style>
        .topnav {
          overflow: hidden;
          background-color: #333;
          background-image: linear-gradient(to right, #000a98, #00b8ff);
        }
        
        .topnav a {
          float: left;
          display: block;
          color: #f2f2f2;
          text-align: center;
          padding: 14px 16px;
          text-decoration: none;
          font-size: 17px;
        }
        
        .active {
          background-image: linear-gradient(to right, #000a98, #00b8ff);
          color: white;
        }
        
        .topnav .icon {
          display: none;
        }
        
        .dropdown {
          float: left;
          overflow: hidden;
        }
        
        .dropdown .dropbtn {
          font-size: 17px;
          border: none;
          outline: none;
          color: white;
          padding: 14px 16px;
          background-color: inherit;
          font-family: inherit;
          margin: 0;
        }
        
        .dropdown-content {
          display: none;
          position: fixed;
          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,
        .dropdown:hover .dropbtn {
          background-color: #555;
          color: white;
        }
        
        .dropdown-content a:hover {
          background-color: #ddd;
          color: black;
        }
        
        .dropdown:hover .dropdown-content {
          display: block;
        }
        
        @media screen and (max-width: 600px) {
          .topnav a:not(:first-child),
          .dropdown .dropbtn {
            display: none;
          }
          .topnav a.icon {
            float: right;
            display: block;
          }
        }
        
        @media screen and (max-width: 600px) {
          /* .topnav.responsive {
            position: relative;
        } */
          .topnav.responsive .icon {
            position: fixed;
            right: 0;
            top: 0;
          }
          .topnav.responsive a {
            float: none;
            display: block;
            text-align: left;
          }
          .topnav.responsive .dropdown {
            float: none;
          }
          .topnav.responsive .dropdown-content {
            position: relative;
          }
          .topnav.responsive .dropdown .dropbtn {
            display: block;
            width: 100%;
            text-align: left;
          }
        }
        
        body {
          padding-top: 16px;
        }
        
        .sticky {
          position: fixed;
          top: 0;
          width: 100%;
        }
        
        .sticky+body {
          padding-top: 60px;
        }
      </style>
    </head>
    
    <body>
      <div class="topnav" id="myTopnav">
        <a href="#home" class="active">Home</a>
        <a href="#news">News</a>
        <a href="#contact">Contact</a>
        <div class="dropdown">
          <button class="dropbtn">
                        Dropdown
                        <i class="fa fa-caret-down"></i>
                    </button>
          <div class="dropdown-content">
            <a href="#">Link 1</a>
            <a href="#">Link 2</a>
            <a href="#">Link 3</a>
          </div>
        </div>
        <a href="#about">About</a>
        <a href="javascript:void(0);" style="font-size: 15px" class="icon" onclick="reactiveTopNav()">&#9776;</a
                >
            </div>
            <script>
                function reactiveTopNav() {
                    const topNav = document.getElementById("myTopnav");
    
                    topNav.classList.contains("responsive")
                        ? topNav.classList.remove("responsive")
                        : topNav.classList.add("responsive");
                }
                window.onscroll = function () {
                    stickyTopNav();
                };
    
                var navbar = document.getElementById("myTopnav");
                var sticky = navbar.offsetTop;
    
                function stickyTopNav() {
                    if (window.pageYOffset >= sticky) {
                        navbar.classList.add("sticky");
                    } else {
                        navbar.classList.remove("sticky");
                    }
                }
            </script>
            <div style="padding-left: 16px">
                <h2>Responsive Topnav with Dropdown</h2>
    
                <p>Resize the browser window to see how it works.</p>
                <p>Hover over the dropdown button to open the dropdown menu.</p>
                <h2>Responsive Topnav with Dropdown</h2>
                <p>Resize the browser window to see how it works.</p>
                <p>Hover over the dropdown button to open the dropdown menu.</p>
                <h2>Responsive Topnav with Dropdown</h2>
                <p>Resize the browser window to see how it works.</p>
                <p>Hover over the dropdown button to open the dropdown menu.</p>
                <h2>Responsive Topnav with Dropdown</h2>
                <p>Resize the browser window to see how it works.</p>
                <p>Hover over the dropdown button to open the dropdown menu.</p>
            </div>
        </body>
    </html>