Collapse / Expand Sidebar Menu Using JavaScript, HTML, & CSS
Tags: javascript, webdev, beginners, tutorial
Published: 2022-07-25 16:16:05

Learn how to collapse / expand a sidebar menu using JavaScript, HTML, & CSS only. In this tutorial, we will build a fully responsive sidebar menu that can be expanded and collapsed using a button. This is commonly seen on modern administration dashboards.

Using CSS and JS we make our sidebar menu look tremendous and functional with a search bar/popup. I also tested and coded for responsive media queries so not only does it work on desktops, but also on mobile phones so your sidebar panel can look great for your entire project. Keep in mind this is a look-only tutorial but you can use it as the basis for your entire project's navigation.

## View This On YouTube
{% youtube -vQIcBKdhyk %}

## Folder Structure

```
index.html
/images
   avatar.jpg
   logo.png
   mobile.svg
/sass
   style.scss
/js
   init.js
/css (generated by Sass)
   style.css
   style.min.css
```

## Our HTML

The HTML of this tutorial is very simple. Essentially, we have a sidebar with a top and a bottom, then a main for the page content. This tutorial will not use BootStrap for its styling but we will use the icons so make sure to either use their icons by adding a reference to the bootstrap icons stylesheet or by adding your own.


```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Collapse / Expand Sidebar Tutorial</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.3/font/bootstrap-icons.css">
    <link rel="stylesheet" href="/css/style.min.css">
</head>
<body>
    <div class="container">
        <div class="sidebar">
            <div class="sidebartop">
                <div class="logo">
                    <img src="/images/logo.png" alt="">
                </div>
                <div class="logo-mobile">
                    <img src="/images/mobile.svg" alt="">
                </div>
                <div class="menu">
                    <i class="bi bi-list"></i>
                </div>
            </div>
            <div class="search">
                <form action="">
                    <button type="submit" class="callSearch"><i class="bi bi-search"></i></button><input type="text" placeholder="Search" class="searchInput">
                </form>
            </div>
            <nav>
                <ul>
                    <li><a href="#"><i class="bi bi-speedometer"></i><span class="text">Dashboard</span></a></li>
                    <li><a href="#"><i class="bi bi-file-earmark"></i><span class="text">Pages</span></a></li>
                    <li><a href="#"><i class="bi bi-pencil-square"></i><span class="text">Posts</span></a></li>
                    <li><a href="#"><i class="bi bi-people"></i><span class="text">Users</span></a></li>
                    <li><a href="#"><i class="bi bi-gear"></i><span class="text">Settings</span></a></li>
                </ul>
            </nav>
            <div class="account">
                <div class="avatar">
                    <img src="/images/avatar.jpg" alt="">
                </div>
                <div class="name">
                    <h4>The DevDrawer</h4>
                    Adminstrator
                </div>
                <div class="logout">
                    <a href="#"><i class="bi bi-box-arrow-left"></i></a>
                </div>
            </div>
        </div>
        <div class="main">
            [page content here]
        </div>
    </div>
    <div class="searchWindow">
        <button type="button" class="cancelSearch"><i class="bi bi-x"></i></button>
        <h2>Search Our Site</h2>
        <form action="">
            <input type="text" placeholder="Enter your text">
            <button type="submit">Search</button>
        </form>
    </div>
    <script src="/js/init.js"></script>
</body>
</html>
```

## Our Basic Sass
The stylesheet is an important part of this tutorial so you can copy over what I have below or use the classes and make them match your own branding.

There are a lot of styles but the following should get you started with placement and layout. I am using Open Sans for the font so I import that directly from Google Fonts.

Please keep in mind, that I have a Sass addon in my VS Code that helps generate the style.css and style.min.css that are referenced in the HTML code. You can download it here if you do not have it already: https://marketplace.visualstudio.com/items?itemName=glenn2223.live-sass

```css
@import url('https://fonts.googleapis.com/css2?family=Open+Sans&display=swap');

$primary-color:#333;
$white: #fff;

body {
    color:$primary-color;
    padding:0;
    margin:0;
    position: relative;
    min-height:100vh;
    overflow:hidden;
    font-family: 'Open Sans', sans-serif;
    font-size:14px;
}

.container {
    display:flex;
    flex-flow:row wrap;
    .sidebar {
        background-color:$primary-color;
        color:$white;
        width:30%;
        height:100%;
        padding:0 1rem;
        position: fixed;
        top:0;
        left:0;
        transition: width .10s ease-in-out;
        a {
            color:$white;
            text-decoration: none;
        }
        .sidebartop{
            display:flex;
            flex-direction: row;
            justify-content: space-between;
            align-items: flex-start;
            padding-top:1rem;
            height:55px;
            .logo {
                width:70%;
                img {
                    height:auto;
                    width:100%;
                }
            }
            .menu {
                width:20%;
                text-align:end;
                i {
                    cursor:pointer;
                    font-size:1.75rem;
                }
            }
            .logo-mobile {
                display:none;
            }
        }
        .search {
            display:flex;
            flex-direction:row;
            justify-content: space-between;
            align-items: flex-start;
            position: relative;
            margin:1rem 0;
            button {
                cursor:pointer;
                width:auto;
                background-color:transparent;
                border:0;
                color:$white;
                position: absolute;
                font-size:1.25rem;
                left:1.5rem;
                top:50%;
                transform: translate(-50%, -50%);
            }
            input {
                background-color:lighten($primary-color, 5%);
                border:0px;
                padding:1rem;
                border-radius: .5rem;
                width:calc(100% + 1rem);
                &::placeholder {
                    padding-left:1.5rem;
                }
            }
        }
        nav {
            ul {
                padding:0;
                margin:0;
                list-style:none;
                li {
                    display:block;
                    align-items:center;
                    padding:1.25rem 0;
                    position: relative;
                    background-color:transparent;
                    transition: background-color .25s ease-in-out;
                    a {
                        display:block;
                        i {
                            font-size:1.25rem;
                        }
                        .text {
                            position: relative;
                            left:1rem;
                            top:-.2rem;
                        }
                    }
                }
            }
        }
        .account {
            display:flex;
            justify-content: space-between;
            align-content:center;
            align-items:center;
            width:calc(100% - 2rem);
            position: absolute;
            bottom:1rem;
            .avatar {
                margin-right:1rem;
                width:20%;
                img {
                    border-radius:50%;
                    height:50px;
                    width:50px;
                }
            }
            .name {
                flex: 1 1 auto;
                h4 {
                    padding:0;
                    margin:0;
                }
            }
            .logout {
                flex: 1 1 auto;
                text-align: end;
                i {
                    font-size:1.5rem;
                }
            }
        }
    }
    .main {
        margin-left:calc(30% + 2rem);
        padding:1rem;
    }
}
```

## Our JS

Our JS is what adds or removes our short class. Essentially, if will take the menu click and add a body class that shows the sidebar collapsed or expanded. You can run the code below and you should see a new class being added to the HTML body.

**NOTE:** You will not see the changes to the styles yet because we have not added the `short` class to our Sass but you should see the class being added.

**NOTE:** The JS below also adds a local storage item so that it will keep the selected state of the sidebar for the user.

```js
const menu = document.querySelector(".menu"); // get menu item for click event

menu.addEventListener("click", function () {
	expandSidebar();
	showHover();
	getSearch();
});

/**
 * expand sidebar if it is short, otherwise collapse it
 */
function expandSidebar() {
	document.querySelector("body").classList.toggle("short");
	let keepSidebar = document.querySelectorAll("body.short");
	if (keepSidebar.length === 1) {
		localStorage.setItem("keepSidebar", "true");
	} else {
		localStorage.removeItem("keepSidebar");
	}
}

/**
 * show hover effect on sidebar
 */
function showHover() {
	const li = document.querySelectorAll(".short .sidebar li a");
	if (li.length > 0) {
		li.forEach(function (item) {
			item.addEventListener("mouseover", function () {
				const text = item.querySelector(".text");
				text.classList.add("hover");
			});
			item.addEventListener("mouseout", function () {
				const text = item.querySelector(".text");
				text.classList.remove("hover");
			});
		});
	}
}

/**
 * get search button click if short sidebar or mobile
 */
function getSearch() {
	document.querySelector(".callSearch").addEventListener("click", function (e) {
		e.preventDefault();
		if (
			document.querySelector("body").classList.contains("short") ||
			window.innerWidth <= 844
		) {
			document.querySelector(".searchWindow").classList.toggle("active");
		}
	});
	document
		.querySelector(".cancelSearch")
		.addEventListener("click", function () {
			document.querySelector(".searchWindow").classList.toggle("active");
		});
}

/**
 * check local storage for keep sidebar
 */
function showStoredSidebar() {
	if (localStorage.getItem("keepSidebar") === "true") {
		document.querySelector("body").classList.add("short");
		showHover();
		getSearch();
	}
}

showStoredSidebar(); // show sidebar if stored in local storage

```

## Adding Our Short Class to Sass

Now that we have the short class being added to the body of the HTML, we need to style it. So open your style.scss file and add the following to the bottom of it (outside of your current CSS).

This class will handle the collapse / expand look as well as hide unneeded items in the sidebar.

```css
.short {
    .sidebar {
        width:5%;
        text-align:center;
        .logo, .searchInput, .text, .avatar, .name {
            display:none;
        }
        .sidebartop {
            display:block;
            height:75px;
            .logo-mobile {
                display:none;
            }
            .menu {
                width:100%;
                text-align:center;
            }
        }
        .text.hover {
            display: block !important;
            background-color:rgba(255,255,255,.9);
            color:$primary-color;
            padding:.5rem;
            box-shadow: 1px 1px 5px 0 rgba(0,0,0,.25);
            position: absolute;
            left:3rem;
            top:1rem;
            border-radius:.25rem;
        }
        .account {
            display:block;
            .logout {
                width:100%;
                text-align:center;
            }
        }
        .search {
            margin:1.75rem -.2rem;
        }
    }
    .main {
        margin-left:calc(5% + 2rem)
    }
}
```

Also, you should now be able to over the icons on the collapsed sidebar to see the hover text associated with them.

## Search Popup

In the JS above, we added a function for a search that was unused at that time, but now we need to create a popup when the collapsed sidebar search icon is clicked since the search itself is not available from that screen.

So, first, let's add the Sass to show the popup correctly.

```css
.searchWindow{
    position: fixed;
    height:100vh;
    width:100vw;
    background-color:rgba(51,51,51,.9);
    z-index:1;
    top:0;
    padding:1rem;
    text-align:center;
    color:$white;
    padding-top:20vh;
    display:none;
    input {
        background-color:darken($white, 25%);
        border:0px;
        padding:1rem .5rem;
        border-radius:.5rem;
        width:60vw;
        &::placeholder {
            padding-left:.5rem;
        }
    }
    button {
        background-color:transparent;
        border:2px solid $white;
        font-size:1rem;
        padding:1rem 2rem;
        color:$white;
        border-radius: .5rem;
        cursor:pointer;
        &.cancelSearch {
            border:0px;
            font-size:2rem;
            position: absolute;
            top:0;
            right:2vw;
        }
    }
    &.active {
        display:block;
    }
}

```

Now, using the JS function above and the CSS you should see a pop-over for the search window when you click the search icon on the collapsed sidebar.

## Mobile Styling

Finally, we need to add mobile styling. This styling does not cover every type of mobile device but you should be able to use it to cover at least an iPhone for now. You can modify the styles to cover other screen sizes.

Below we cover the vertical and horizontal views on a phone.

```css
@media (max-width:844px) {
    .container {
        .sidebar {
            width:5%;
            text-align:center;
            .logo, .searchInput, .text, .avatar, .name {
                display:none;
            }
            .sidebartop {
                display:block;
                height:auto;
                .logo-mobile {
                    display:block;
                    img {
                        height:auto;
                        width:80%
                    }
                }
                .menu {
                    display:none;
                }
            }
            nav {
                ul {
                    li {
                        padding:0;
                        a {
                            padding:0.6rem 0;
                        }
                    }
                }
            }
            .account {
                display:block;
                .logout {
                    width:100%;
                    text-align:center;
                }
            }
            .search {
                margin:1.5rem -.2rem;
            }
        }
        .main {
            margin-left:calc(5% + 2rem)
        }
    }
}

@media (max-width:390px) {
    .container {
        .sidebar {
            width:8%;
            nav {
                ul {
                    li {
                        padding:0;
                        a {
                            padding:2rem 0;
                        }
                    }
                }
            }
            .search {
                margin-top:4rem;
                margin-bottom:3rem;
                margin-left:-.5rem;
            }
        }
        .main {
            margin-left:calc(8% + 2rem)
        }
    }
    .searchWindow{
        padding:0;
        padding-top:10vh;
        input {
            width:calc(100% - 2rem);
        }
        button {
            width:calc(100% - 1rem);
            margin-top:.5rem;
        }
    }
}
```

## Conclusion

Now, you should be done with your code and you should have a collapsable and expandable sidebar that keeps your settings in a local storage object and that works on mobile. Good luck.

Read more articles on [DevDrawer](https://thedevdrawer.com)