DEV Community

Cover image for Collapse / Expand Sidebar Menu Using JavaScript, HTML, & CSS
The Dev Drawer
The Dev Drawer

Posted on • Updated on

Collapse / Expand Sidebar Menu Using JavaScript, HTML, & CSS

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

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
Enter fullscreen mode Exit fullscreen mode

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.

<!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>
Enter fullscreen mode Exit fullscreen mode

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

@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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

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

Enter fullscreen mode Exit fullscreen mode

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.

.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)
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

.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;
    }
}

Enter fullscreen mode Exit fullscreen mode

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.

@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;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)