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:

📌 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
imgBoxor contenth2tags - 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, othersopacity: 0.5. - Changes active dot color: Picks a random color from
colorsarray.
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