How to Create a Multilevel Dropdown Menu with HTML, CSS, and JavaScript

A well-structured navigation menu is essential for any website. In this tutorial, we will create a multilevel dropdown menu using HTML, CSS, and JavaScript. This menu will have multiple submenus and will be fully responsive for mobile devices.

Final Output Preview

Before we dive into the code, here is a preview of what we will build:

gsap-slider.gif

📌 1. HTML Structure

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>GSAP slider</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
        integrity="sha512-Evv84Mr4kqVGRNSgIGL/F/aIDqQb7xQ2vcrdIwxfjThSH8CSR7PBEakCr51Ck+w+/U6swU2Im1vVX0SVk9ABhg=="
        crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>
<body>
    <h1>GSAP slider</h1>
    <div class="slideContainer">
        <div class="slide">
            <div class="imgBox"><img src="./images/goku.png" alt=""></div>
            <h2>son goku</h2>
            <h2>son goku</h2>
        </div>
        <div class="slide">
            <div class="imgBox"><img src="./images/inosuke.png" alt=""></div>
            <h2>inosuke</h2>
            <h2>inosuke</h2>
        </div>
        <div class="slide">
            <div class="imgBox"><img src="./images/itachi.png" alt=""></div>
            <h2>itachi</h2>
            <h2>itachi</h2>
        </div>
        <div class="slide">
            <div class="imgBox"><img src="./images/naruto.png" alt=""></div>
            <h2>naruto</h2>
            <h2>naruto</h2>
        </div>
        <div class="slide">
            <div class="imgBox"><img src="./images/rengoku.png" alt=""></div>
            <h2>rengoku</h2>
            <h2>rengoku</h2>
        </div>
        <div class="slide">
            <div class="imgBox"><img src="./images/tanjiro.png" alt=""></div>
            <h2>tanjiro</h2>
            <h2>tanjiro</h2>
        </div>
        <div class="slide">
            <div class="imgBox"><img src="./images/zenitsu.png" alt=""></div>
            <h2>zenitsu</h2>
            <h2>zenitsu</h2>
        </div>

        <!-- arrows -->
        <div class="arrows">
            <button class="prev"><i class="fa-solid fa-angles-left"></i></button>
            <button class="next"><i class="fa-solid fa-angles-right"></i></button>
        </div>
        <!-- dots -->
        <div class="dotsContainer"></div>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"
        integrity="sha512-7eHRwcbYkK4d9g/6tD/mhkf++eoTHwpNM9woBxtPUBWm67zeAfFC+HrdoE2GanKeocly/VxeLvIqwvCdk7qScg=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  </body >
</html >

Explanation:

  • Setting Up the Basic HTML Structure
  • add fontawesome and GSAP cdn
  • create slideContainer which work as wrapper of all slider elements
  • create slide which holds all the element of perticualr slide like imgBox or content h2 tags
  • create arrows for navigation
  • create dotsContainer for dot navigation and insert dots using js

📌 2. Styling CSS

Add Global Reset css and boilerplate code

*,
    *::before,
    *::after {
        padding: 0;
        margin: 0;
        box-sizing: border-box;
    }

    body,
    html {
        width: 100%;
        height: 100%;
        position: relative;
    }

Add css for body and h1

body {
        font-family: Verdana, Geneva, Tahoma, sans-serif;
        background-color: #f4f4f4;
        height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
        flex-direction: column;
        padding: 1rem;
    }

    body>h1 {
        text-align: center;
        margin: 1rem auto;
        font-size: clamp(2rem, 3vw, 4rem);
        text-transform: capitalize;
    }

Add css for slideContainer and slide and its elements

.slideContainer {
        max-width: 800px;
        background-color: black;
        color: white;
        overflow: hidden;
        position: relative;
        isolation: isolate;
        height: 100%;
        width: 100%;
    }

    .slideContainer .slide {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }

    .slideContainer .slide .imgBox {
        width: 100%;
    }

    .slideContainer .slide .imgBox img {
        max-height: 500px;
        max-width: 100%;
        width: fit-content;
        height: 100%;
        object-fit: contain;
    }

    .slideContainer .slide h2 {
        position: absolute;
        top: 80%;
        left: 50%;
        transform: translate(-50%, -50%);
        color: #FF00FF;
        font-size: clamp(2rem, 6vw, 8rem);
        text-transform: uppercase;
        z-index: -1;
        white-space: nowrap;
    }

    .slideContainer .slide h2:nth-child(2) {
        color: transparent;
        -webkit-text-stroke: 0.15vw white;
        z-index: 1;
    }

Add css for arrows and its buttons

.slideContainer .arrows {
        width: 100%;
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        padding: 1rem;
        display: flex;
        justify-content: space-between;
        align-items: center;
    }

    .slideContainer .arrows button {
        cursor: pointer;
        background-color: white;
        color: black;
        border: none;
        width: 45px;
        height: 45px;
        border-radius: 50%;
        display: flex;
        justify-content: center;
        align-items: center;
        font-size: 1.2rem;
        transition: 300ms ease;
    }

    .slideContainer .arrows button:hover {
        background-color: rgba(255, 255, 255, 0.733);
    }

    .slideContainer .arrows button:active {
        background-color: rgba(255, 255, 255, 0.877);
        transform: scale(0.9);
    }

Add css for dotsContainer and its dot

.slideContainer .dotsContainer {
        width: 100%;
        position: absolute;
        bottom: 0;
        left: 0;
        display: flex;
        justify-content: center;
        align-items: center;
        gap: 1rem;
        padding: 1rem;
    }

    .slideContainer .dotsContainer .dot {
        width: 25px;
        height: 25px;
        cursor: pointer;
        display: flex;
        justify-content: center;
        align-items: center;
        gap: 1rem;
        background-color: #FF00FF;
        color: white;
    }

📌 3. Add JavaScript For Slider

select all the required html elements in js

const slides = document.querySelectorAll('.slide');
    const prevBtn = document.querySelector('.arrows .prev')
    const nextBtn = document.querySelector('.arrows .next')
    const dotsContainer = document.querySelector('.dotsContainer')

create some js variables for slider

const colors = [
    "#FF00FF", // Magenta
    "#FF4500", // Neon Orange
    "#39FF14", // Neon Green
    "#FF1493", // Deep Pink
    "#FFD700", // Neon Yellow
    "#8A2BE2", // Electric Purple
    "#00FF7F", // Spring Green
    "#FF69B4", // Hot Pink
    "#1E90FF", // Dodger Blue
    "#FF6347", // Neon Tomato
    "#ADFF2F", // Green Yellow
    "#9400D3", // Dark Violet
    "#00BFFF", // Deep Sky Blue
    "#FF00FF", // Fuchsia
];
// slider variables
let currentSlideIndex = 0;
let isAnimating = false;
let autoSlideInetrval;

hide all the slides except first one by using gsap

// hide all the slides at start except first slide
gsap.set(slides, {
    autoAlpha: (i) => i == 0 ? 1 : 0
})
// slider variables
let currentSlideIndex = 0;
let isAnimating = false;
let autoSlideInetrval;

create reusable functions for slider lets call them slider functions

//show slide
    function showSlide() {

    }
    // prev slide
    function prevSlide() {

    }
    //next slide
    function nextSlide() {

    }
    // gotTo slide
    function goToSlide(index) {

    }

insert slide dots using js and select them in js

// insert dots
    slides.forEach((slide, index) => {
        let dot = document.createElement("span");
        dot.setAttribute('class', 'dot');
        let textNode = document.createTextNode(index + 1);
        dot.appendChild(textNode);
        dotsContainer.appendChild(dot);
    })
    // select all dots
    let dots = dotsContainer.querySelectorAll('.dot');

call slider functions on click of next ,prev and dot buttons

nextBtn.addEventListener('click', nextSlide)
    prevBtn.addEventListener('click', prevSlide)
    dots.forEach((dot, index) => {
        dot.addEventListener('click', () => {
            goToSlide(index);
        })
    })

update slider functions code

// prev slide
    function prevSlide() {
        // handle multiple clicks
        if (isAnimating) return
        // currentSlideIndex --;
        currentSlideIndex = (currentSlideIndex - 1 + slides.length) % slides.length;
        showSlide()
        resetAutoslide()
    }
    //next slide
    function nextSlide() {
        if (isAnimating) return
        // currentSlideIndex++;
        currentSlideIndex = (currentSlideIndex + 1) % slides.length;
        showSlide()
        resetAutoslide()
    }
    // gotTo slide
    function goToSlide(index) {
        if (isAnimating) return
        currentSlideIndex = index;
        showSlide()
        resetAutoslide()
    }

update showSlide function code

updateDots(); 
    // function to show slide
    function showSlide() {
        isAnimating = true;
        //show current slide and hide others
        gsap.set(slides, {
            autoAlpha: (i) => i == currentSlideIndex ? 1 : 0
        })
        //add animation to the current slide elements
        let currentSlide = slides[currentSlideIndex];
        let ImgBox = currentSlide.querySelector('.imgBox');
        let headings = currentSlide.querySelectorAll('h2');
        gsap
            .timeline({
                onComplete: () => {
                    isAnimating = false;
                }
            })
            .from(ImgBox, {
                y: 100,
                opacity: 0,
                duration: 0.5
            }).from(headings, {
                y: -100,
                opacity: 0,
                duration: 0.5,
                delay: -0.5
            }).to(headings[1], {
                color: gsap.utils.random(colors),
                delay: -1
            })
        // update dots
        updateDots()
    }

    // update dots
    function updateDots() {
        gsap.set(dots, {
            opacity: (i) => i === currentSlideIndex ? 1 : 0.5,
            duration: 0.2,
            backgroundColor: (i) => i === currentSlideIndex ? gsap.utils.random(colors) : colors[0]
        })
    }

Explanation:

  • Prevents multiple animations by setting isAnimating = true.
  • Shows only the current slide using:

gsap.set(slides, { autoAlpha: (i) => i == currentSlideIndex ? 1 : 0 })

  • Selects key elements in the slide:

let currentSlide = slides[currentSlideIndex];
    let ImgBox = currentSlide.querySelector('.imgBox');
    let headings = currentSlide.querySelectorAll('h2');

  • Creates an animation sequence using gsap.timeline():
    • Image (ImgBox) slides up & fades in:

.from(ImgBox, { y: 100, opacity: 0, duration: 0.5 })

  • Headings (h2) slide down & fade in:

.from(headings, { y: -100, opacity: 0, duration: 0.5, delay: -0.5 })

  • Changes color of the second heading randomly:

.to(headings[1], { color: gsap.utils.random(colors), delay: -1 })

  • Marks animation as complete: isAnimating = false.
  • Calls updateDots() to update indicators.

2. updateDots() Function (Navigation Indicators)

  • Updates opacity: Active dot opacity: 1, others opacity: 0.5.
  • Changes active dot color: Picks a random color from colors array.

gsap.set(dots, {
        opacity: (i) => i === currentSlideIndex ? 1 : 0.5,
        backgroundColor: (i) => i === currentSlideIndex ? gsap.utils.random(colors) : colors[0]
    })

add functionality to autoslide

//reset autoslide
    function resetAutoslide() {
        clearInterval(autoSlideInetrval);
        autoSlideInetrval = setInterval(() => {
            nextSlide()
        }, 3000)
    }
    // auto slide
    autoSlideInetrval = setInterval(() => {
        nextSlide()
    }, 3000)

full code

<!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>GSAP slider</title>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
            integrity="sha512-Evv84Mr4kqVGRNSgIGL/F/aIDqQb7xQ2vcrdIwxfjThSH8CSR7PBEakCr51Ck+w+/U6swU2Im1vVX0SVk9ABhg=="
            crossorigin="anonymous" referrerpolicy="no-referrer" />
    </head>
    <style>
        *,
        *::before,
        *::after {
            padding: 0;
            margin: 0;
            box-sizing: border-box;
        }
    
        body,
        html {
            width: 100%;
            height: 100%;
            position: relative;
        }
    
        body {
            font-family: Verdana, Geneva, Tahoma, sans-serif;
            background-color: #f4f4f4;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            padding: 1rem;
        }
    
        body>h1 {
            text-align: center;
            margin: 1rem auto;
            font-size: clamp(2rem, 3vw, 4rem);
            text-transform: capitalize;
        }
    
        .slideContainer {
            max-width: 800px;
            background-color: black;
            color: white;
            overflow: hidden;
            position: relative;
            isolation: isolate;
            height: 100%;
            width: 100%;
        }
    
        .slideContainer .slide {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }
    
        .slideContainer .slide .imgBox {
            width: 100%;
        }
    
        .slideContainer .slide .imgBox img {
            max-height: 500px;
            max-width: 100%;
            width: fit-content;
            height: 100%;
            object-fit: contain;
        }
    
        .slideContainer .slide h2 {
            position: absolute;
            top: 80%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #FF00FF;
            font-size: clamp(2rem, 6vw, 8rem);
            text-transform: uppercase;
            z-index: -1;
            white-space: nowrap;
        }
    
        .slideContainer .slide h2:nth-child(2) {
            color: transparent;
            -webkit-text-stroke: 0.15vw white;
            z-index: 1;
        }
    
        .slideContainer .arrows {
            width: 100%;
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            padding: 1rem;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
    
        .slideContainer .arrows button {
            cursor: pointer;
            background-color: white;
            color: black;
            border: none;
            width: 45px;
            height: 45px;
            border-radius: 50%;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 1.2rem;
            transition: 300ms ease;
        }
    
        .slideContainer .arrows button:hover {
            background-color: rgba(255, 255, 255, 0.733);
        }
    
        .slideContainer .arrows button:active {
            background-color: rgba(255, 255, 255, 0.877);
            transform: scale(0.9);
        }
    
        .slideContainer .dotsContainer {
            width: 100%;
            position: absolute;
            bottom: 0;
            left: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            gap: 1rem;
            padding: 1rem;
        }
    
        .slideContainer .dotsContainer .dot {
            width: 25px;
            height: 25px;
            cursor: pointer;
            display: flex;
            justify-content: center;
            align-items: center;
            gap: 1rem;
            background-color: #FF00FF;
            color: white;
        }
    </style>
    
    <body>
        <h1>GSAP slider</h1>
        <div class="slideContainer">
            <div class="slide">
                <div class="imgBox"><img src="./images/goku.png" alt=""></div>
                <h2>son goku</h2>
                <h2>son goku</h2>
            </div>
            <div class="slide">
                <div class="imgBox"><img src="./images/inosuke.png" alt=""></div>
                <h2>inosuke</h2>
                <h2>inosuke</h2>
            </div>
            <div class="slide">
                <div class="imgBox"><img src="./images/itachi.png" alt=""></div>
                <h2>itachi</h2>
                <h2>itachi</h2>
            </div>
            <div class="slide">
                <div class="imgBox"><img src="./images/naruto.png" alt=""></div>
                <h2>naruto</h2>
                <h2>naruto</h2>
            </div>
            <div class="slide">
                <div class="imgBox"><img src="./images/rengoku.png" alt=""></div>
                <h2>rengoku</h2>
                <h2>rengoku</h2>
            </div>
            <div class="slide">
                <div class="imgBox"><img src="./images/tanjiro.png" alt=""></div>
                <h2>tanjiro</h2>
                <h2>tanjiro</h2>
            </div>
            <div class="slide">
                <div class="imgBox"><img src="./images/zenitsu.png" alt=""></div>
                <h2>zenitsu</h2>
                <h2>zenitsu</h2>
            </div>
    
            <!-- arrows -->
            <div class="arrows">
                <button class="prev"><i class="fa-solid fa-angles-left"></i></button>
                <button class="next"><i class="fa-solid fa-angles-right"></i></button>
            </div>
            <!-- dots -->
            <div class="dotsContainer"></div>
        </div>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"
            integrity="sha512-7eHRwcbYkK4d9g/6tD/mhkf++eoTHwpNM9woBxtPUBWm67zeAfFC+HrdoE2GanKeocly/VxeLvIqwvCdk7qScg=="
            crossorigin="anonymous" referrerpolicy="no-referrer"></script>
        <script>
            const slides = document.querySelectorAll('.slide');
            const prevBtn = document.querySelector('.arrows .prev')
            const nextBtn = document.querySelector('.arrows .next')
            const dotsContainer = document.querySelector('.dotsContainer')
            const colors = [
                "#FF00FF", // Magenta
                "#FF4500", // Neon Orange
                "#39FF14", // Neon Green
                "#FF1493", // Deep Pink
                "#FFD700", // Neon Yellow
                "#8A2BE2", // Electric Purple
                "#00FF7F", // Spring Green
                "#FF69B4", // Hot Pink
                "#1E90FF", // Dodger Blue
                "#FF6347", // Neon Tomato
                "#ADFF2F", // Green Yellow
                "#9400D3", // Dark Violet
                "#00BFFF", // Deep Sky Blue
                "#FF00FF", // Fuchsia
            ];
            // hide all the slides at start except first slide
            gsap.set(slides, {
                autoAlpha: (i) => i == 0 ? 1 : 0
            })
            // slider variables
            let currentSlideIndex = 0;
            let isAnimating = false;
            let autoSlideInetrval;
            // insert dots
            slides.forEach((slide, index) => {
                let dot = document.createElement("span");
                dot.setAttribute('class', 'dot');
                let textNode = document.createTextNode(index + 1);
                dot.appendChild(textNode);
                dotsContainer.appendChild(dot);
            })
            // select all dots
            let dots = dotsContainer.querySelectorAll('.dot');
            updateDots();
            // function to show slide
            function showSlide() {
                isAnimating = true;
                //show current slide and hide others
                gsap.set(slides, {
                    autoAlpha: (i) => i == currentSlideIndex ? 1 : 0
                })
                //add animation to the current slide elements
                let currentSlide = slides[currentSlideIndex];
                let ImgBox = currentSlide.querySelector('.imgBox');
                let headings = currentSlide.querySelectorAll('h2');
                gsap
                    .timeline({
                        onComplete: () => {
                            isAnimating = false;
                        }
                    })
                    .from(ImgBox, {
                        y: 100,
                        opacity: 0,
                        duration: 0.5
                    }).from(headings, {
                        y: -100,
                        opacity: 0,
                        duration: 0.5,
                        delay: -0.5
                    }).to(headings[1], {
                        color: gsap.utils.random(colors),
                        delay: -1
                    })
                // update dots
                updateDots()
            }
            // prev slide
            function prevSlide() {
                // handle multiple clicks
                if (isAnimating) return
                // currentSlideIndex --;
                currentSlideIndex = (currentSlideIndex - 1 + slides.length) % slides.length;
                showSlide()
                resetAutoslide()
            }
            //next slide
            function nextSlide() {
                if (isAnimating) return
                // currentSlideIndex++;
                currentSlideIndex = (currentSlideIndex + 1) % slides.length;
                showSlide()
                resetAutoslide()
            }
            // gotTo slide
            function goToSlide(index) {
                if (isAnimating) return
                currentSlideIndex = index;
                showSlide()
                resetAutoslide()
            }
            // update dots
            function updateDots() {
                gsap.set(dots, {
                    opacity: (i) => i === currentSlideIndex ? 1 : 0.5,
                    duration: 0.2,
                    backgroundColor: (i) => i === currentSlideIndex ? gsap.utils.random(colors) : colors[0]
                })
            }
            //reset autoslide
            function resetAutoslide() {
                clearInterval(autoSlideInetrval);
                autoSlideInetrval = setInterval(() => {
                    nextSlide()
                }, 3000)
            }
            // auto slide
            autoSlideInetrval = setInterval(() => {
                nextSlide()
            }, 3000)
            nextBtn.addEventListener('click', nextSlide)
            prevBtn.addEventListener('click', prevSlide)
            dots.forEach((dot, index) => {
                dot.addEventListener('click', () => {
                    goToSlide(index);
                })
            })
        </script>
    </body>
    
    </html>

Tags : #JavaScript slider #infinite loop slider #JavaScript carousel #auto-slide JavaScript #smooth transition slider #JavaScript image slider #how to create a slider in JavaScript #GSAP slider #GSAP animations #image carousel #slide transitions #and interactive navigation #how to create a slider in GSAP

Recent blog Posts

search post

search video tutorial