Creating motion for a collapsible sidebar

Trying to design a basic page layout with a collapsible menu next to the main content. Seeking a way to smoothly collapse the menu for a better user experience.

To illustrate, consider this example:

#container { display: flex; position: fixed; height: 100%; } #menu { background: yellow; } #toggle { cursor: pointer; user-select: none } #menu.collapsed .content { display: none; }
<div id="container">
  <div id="menu">
    <div id="toggle" onclick="document.getElementById('menu').classList.toggle('collapsed')">X</div>
    <div class="content">Menu content</div>
  </div>
  <div class="content">Main content goes here</div>
</div>

Functionality-wise, clicking the X toggles the menu as expected, but the abrupt transition is not ideal. How can we achieve a smoother closing effect?

One approach involves setting a specific width for the menu and utilizing a transition: width property in the stylesheet:

The modified CSS code is as follows:

#container { display: flex; position: fixed; height: 100%; } #menu { background: yellow; } #toggle { cursor: pointer; user-select: none } #menu.collapsed .content { overflow: hidden; } #menu .content { transition: width 1s; width: 100px; } #menu.collapsed .content { width: 0px; }
<div id="container">
  <div id="menu">
    <div id="toggle" onclick="document.getElementById('menu').classList.toggle('collapsed')">X</div>
    <div class="content">Menu content</div>
  </div>
  <div class="content">Main content goes here</div>
</div>

However, this solution has some limitations:

  1. Difficulty in determining the initial width (100px here). Would prefer a dynamic width based on the content size. Removing width: 100px breaks the transition, and width: 100% does not work properly either.

  2. During menu closure, text reflows which appears unattractive. Looking for ways to avoid this issue.

  3. Potential inefficiency of maintaining a zero-width menu in the DOM compared to using display: none, as rendering may still occur.

Seeking alternative methods to animate the opening and closing of the menu for a seamless experience.

(Referenced a similar question here: Collapsible flexible-width sidebar using only CSS. This suggests using max-width instead of width, resolving one issue but introducing a transition delay when max-width is high.)

Answer №1

If for any reason you prefer not to set a fixed width for the menu, then you will need to perform some JavaScript manipulation. The main focus will be on setting the menu width accordingly. Below is an example that does not consider adaptive resizing, but can serve as a starting point:

document.querySelectorAll('.menu .toggle').forEach(el => {
  el.addEventListener('click', () => {
    const container = el.closest('.container');
    const menuContent = container.querySelector('.menu-content');
    const content = menuContent.querySelector('.content');
    if ( container.classList.contains('collapsed') ) {
      menuContent.style.width = content.getBoundingClientRect().width + 'px';
    } else {
      menuContent.style.width = 0;
    }
    container.classList.toggle('collapsed');
  });
})

function setMenuWidth() {
  document.querySelectorAll('.menu-content').forEach(el => {
    const container = el.closest('.container');
    if ( container.classList.contains('collapsed') ) {
      return;
    }
    const menuContent = container.querySelector('.menu-content');
    const content = menuContent.querySelector('.content');
    const width = content.getBoundingClientRect().width;
    content.style.removeProperty('width');
    el.style.removeProperty('width');
    content.style.width = width + 'px';
    el.style.width = width + 'px';
  })
}

setMenuWidth();
.container {
  display: flex;
  position: fixed;
  height: 100%;
}

.menu {
  background: yellow;
  flex:none;
}

.menu .toggle {
  cursor: pointer;
  user-select: none
}

.menu-content {
  transition: width .4s;
  overflow: hidden;
}

.menu-content .content {
  transition: opacity .4s;
}

.container.collapsed .menu-content .content {
  opacity: 0;
}
<div class="container">
  <div class="menu">
    <div class="toggle">X</div>
    <div class="menu-content">
      <div class="content">Menu content</div>
    </div>
  </div>
   <div class="content">Main content goes here</div>
</div>

Answer №2

To create an animated sidebar, you can utilize a combination of CSS properties such as transition: width;, position: fixed, and

white-space: nowrap; overflow: hidden;
to achieve a smooth effect:

function openNav() {
  document.getElementById("mySidenav").style.width = "250px";
}

function closeNav() {
  document.getElementById("mySidenav").style.width = "0";
}
.sidenav {
  height: 100%;
  width: 0;
  position: fixed;
  z-index: 1;
  top: 0;
  left: 0;
  background-color: #111;
  overflow-x: hidden;
  transition: 0.5s;
  padding-top: 60px;
}

.sidenav a {
  padding: 8px 8px 8px 32px;
  text-decoration: none;
  font-size: 25px;
  color: #818181;
  display: block;
  transition: 0.3s;
}

.sidenav a:hover {
  color: #f1f1f1;
}

.sidenav .closebtn {
  position: absolute;
  top: 0;
  right: 25px;
  font-size: 36px;
  margin-left: 50px;
}
<div id="mySidenav" class="sidenav">
  <a href="javascript:void(0)" class="closebtn" onclick="closeNav()">&times;</a>
  <a href="#">About</a>
  <a href="#">Services</a>
  <a href="#">Clients</a>
  <a href="#">Contact</a>
</div>

<h2>Animated Sidenav Example</h2>
<p>Click on the element below to open the side navigation menu.</p>
<span style="font-size:30px;cursor:pointer" onclick="openNav()">&#9776; open</span>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Incorporating components into Vue while utilizing Flask poses a challenge

Currently, I am working on a project that involves Flask and Vue. I am attempting to add a carousel to my website, but I am running into difficulties with creating and importing components for Vue when following tutorials. Here is my current file structure ...

Is it possible to have multiple links that redirect a video to different sources?

I've been experimenting with creating multiple links that can change the source of the video being played on a player when clicked. Although I have come close to achieving my desired outcome, there is still one hurdle remaining: Here's the HTML ...

Grammarly is seamlessly inserting padding into my body element

After setting up my website on GitHub, I ran into an issue where the Grammarly Chrome extension was automatically adding a padding-top attribute to the body tag. <body data-new-gr-c-s-check-loaded="14.1028.0" data-gr-ext-installed="" ...

Is it possible for a cybercriminal to falsify AJAX / POST requests?

I'm still new to web development and learning as I go. In the process of building my site (using LAMP on an Ubuntu 16.04 VPS), I've set up an admin page where I can review and manage new users, posts, and more. With a couple of basic AJAX functi ...

Navigate to a new page seamlessly without the need for refreshing the current page in Django

Is there a way to navigate to another page without refreshing after clicking on a link to a different URL? For example, if I have a list of books displayed and I click on one of the books, can it redirect me to the selected book page without reloading the ...

Tips for adding a search bar to a material-ui MenuItem?

Can someone guide me on how to add a search input within the component? I reviewed the material-ui documentation, tried various approaches, but haven't been successful yet. Here's the code for the demo What I attempted: const searchBar = `${& ...

What steps can be taken to ensure that the scale() function is given the highest priority

Trying to develop a program that generates a canvas graph based on some calculated data. To adjust for larger numbers, I attempted to scale the graph using ctx.scale();. However, every time I do this, the canvas goes blank! I even tried scaling the canvas ...

Looking for the next jQuery class that is not a sibling to toggle the slide

By default, all elements with the class .setupData are hidden. I want to be able to toggle the immediate (next in DOM) .setupData element when I click on an element with the class .setupTitle. Even though I've tried multiple variations of the follow ...

What is the best way to manage the ordering of float elements?

Check out my code snippet below: .wrapper { border: 1px solid red; padding-left: 20px; } .table { display: table; width: 100%; } .table > div { display: table-cell; border: 1px solid green; min-width: 190px; } @media screen and ( ...

Error: The provided `anchorEl` property for this component is invalid

While working on my React 18.2 app with MUI 5.10.5, I encountered an issue trying to create a <Menu /> element that should open when a button is clicked. The menu does appear, but the positioning seems off as it displays in the top-left corner of the ...

alter objective response

Currently, I am in the process of developing an educational game for children inspired by the classic "whack-a-mole" style. In this game, kids are presented with a math question and must click on the correct number that appears to solve it. For instance, i ...

storing label text from checkboxes as an array in a Vue.js component

I need to save the selected checkbox label names in an array. <template> <div class="q-gutter-sm"> <q-checkbox dense v-model="add_info" label="Data Structures and Algorithms" color="teal&quo ...

Bootstrap Datepicker, ensuring error-free date selections for check-in and check-out dates

I have encountered some issues while using the bootstrap date picker. If you visit this website, you will find an example of a check-in and check-out selector. When I choose Feb 8th as my arrival date and Feb 9th as my departure date, and then go back to ...

Empty array is logged by the server after sending a JavaScript variable through post request

When I use console.log(request.body), the terminal displays {} instead of logging the variable ownerSteamId. Here is my code: Server-side JavaScript: const express = require('express'); const app = express(); const bodyParser = require('bod ...

Unspecified locale with Next.js router

Despite extensive research and multiple attempts, I have not been able to find a solution to my problem. That's why I am reaching out for help again. Currently, I am working on a local project and I would like to implement Internationalized Routing f ...

Displaying a list with a width of 100% inside a user interface that

I am working with a list that has a horizontal scrollbar, and I want to change the color of the list items. However, when I scroll, the styles on the items are not visible because the width of the items does not cover the full list width. Link to image ...

Is it possible for an object to receive notifications when a component object undergoes changes in Angular 2/4?

When working with Angular components, it's possible to pass a variable or object as @Input(). The component will be notified whenever the value of this input changes, which is pretty cool... I'm currently developing an application that features ...

Cordova Geolocation now displaying incorrect latitude and longitude values as NAN

Recently starting out with javascript and Cordova, I decided to develop a basic GPS app in Visual Studio 2015. The goal was simple: get my current position by clicking on the "CURRENT POSITION" button. Testing it in Firefox yielded positive results. Howev ...

React's struggle with implementing flexbox functionality

Struggling to implement a calculator using React and flexbox, but running into issues with my flexbox layout. I've attempted to troubleshoot using the Chrome Dev Tools but haven't made any progress. import React, { Component } from "react"; imp ...

Creating dynamic variable names in JavaScript can be a powerful tool to enhance

I am currently facing a challenge where I need to generate variables dynamically within a loop. I have been researching different methods, including using the eval(); function, but most of what I found only focuses on changing the value inside an existing ...