The sidebar momentarily shrinks before expanding again when the page is loaded

There is a sidebar on my website that can expand or collapse when a button is clicked. I have successfully saved its state in the localStorage, but there is a small issue that I need help with.

When the page loads and there is no saved state in the localStorage, the sidebar collapses briefly and then expands. The default state should be expanded when there is no localStorage state. I want the sidebar to load in the expanded state without collapsing first. I have tried to solve this issue with my code but have been unsuccessful. I even tried combining my code with code from a post on SO, but it still doesn't work.

If you want to look at the full code, you can find it on Codepen.

Here is the code snippet (please note that localStorage won't work in this environment):

$('document').ready(function() {

    if (typeof window.isMinified === "undefined") {
      window.isMinified = false;
    }


    const body = $('#body');
    $("#sidebar-toggler").on("click", function () {
        
        if (window.isMinified === false) {

            // localStorage.setItem('menu-closed', !$(body).hasClass("sidebar-minified"));
            body.removeClass("sidebar-minified-out").addClass("sidebar-minified");
            window.isMinified = true;

        } else {

            // localStorage.setItem('menu-closed', !$(body).hasClass("sidebar-minified"));
            body.removeClass("sidebar-minified").addClass("sidebar-minified-out");
            window.isMinified = false;

        }
            
    });

    const state = // localStorage.getItem('menu-closed');

    if (state === null) {

        $(body).removeClass('sidebar-minified');

    } else {

        const closed = state === "true" ? true : false;

        if (!closed) {
            $(body).removeClass('sidebar-minified');
        }

    }
});
#body {
  background: #fff;
  transition: all 0.3s;
}

aside.left-sidebar{
  background-color: #2c0963;
  height: 100vh;
}

.sidebar-minified-out .left-sidebar {
  width: 180px;
  transition: width .3s ease-in; 
}
.sidebar-minified .left-sidebar {
  width: 75px;
  transition: width .3s ease-in; 
} 


.sidebar-toggle {
    font-weight: 300;
    font-size: 15px;
    cursor: pointer;
    height: 30px;
    position: absolute;
    left: 20%;
    top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body id="body" class="sidebar-minified sidebar-minified-out">
  <aside class="left-sidebar"></aside>

  <button id="sidebar-toggler" class="sidebar-toggle">Collapse/Expand</button>
  
</body>

Answer №1

To reduce the flashing effect, you can instruct the browser to repaint only once, all at once. However, there will always be an initial size for your sidebar, as defined in your markup.

In my example, I utilize two Observers to monitor changes in style and size. Take note of the initial width of the sidebar. You can set the initial width of the sidebar to 0, leave it unassigned, or even style it to match the size of your expanded sidebar, but there will always be an initial repaint.

Lastly, it is advisable to remove the two initial classes from the body.

EDIT:

Upon reviewing the messages logged by the Observers, you will observe a consistent repaint, as mentioned earlier.

After examining this solution from a previous question on handling dark mode flickering, you can apply a similar approach to your Sidebar toggler implementation.

Instead of adding the CSS class to the body, consider adding it to the html tag. Here’s the complete code:

HTML

<!DOCTYPE html>
<html>
<head>
  <script>
    /* Render blocking script */
    var c = +localStorage.getItem('menu-closed');
    document.documentElement.classList.add(c ? 'sidebar-minified' : 'sidebar-minified-out');
  </script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
  <aside class="left-sidebar"></aside>
  <button id="sidebar-toggler" class="sidebar-toggle">Collapse/Expand</button>
</body>
</html>

JS

$(function() {
  $("#sidebar-toggler").on("click", function (e) {
    var isMinified = !$("html").hasClass("sidebar-minified-out");
    $("html")
      .toggleClass("sidebar-minified", !isMinified)
      .toggleClass("sidebar-minified-out", isMinified);
    localStorage.setItem('menu-closed', +!isMinified);
  });
});

Your CSS will remain unchanged, except for a slight adjustment (removing the #body id).

If you compare the Observed changes, you will see that the second solution, which involves the blocking JS script in the head, only displays the initial Sidebar size, eliminating the initial repaint:

   1st solution                          2nd solution
==============================================================
    Sidebar width: 601                    Closed: false
    jQuery DOM Ready                      Sidebar width: 180
    Closed: false                         jQuery DOM Ready
    Body class changed
    Body style changed
    Sidebar width: 180

(Credits: Roko C. Buljan)

For more information:


The debug functions in my initial example are purely for illustrating the sequence of resize and restyle events within the browser.

Here is some documentation on the Observers:

Answer №2

When the CSS transition is removed, the sidebar immediately expands without any delay. Therefore, when setting CSS classes for the first time, it is recommended to do so without any transitions.

Answer №3

To disable the transition with Javascript and then re-enable it for the click event, you can follow these steps:

Check out this jsfiddle demo for reference.

Start by removing classes in the body tag:

<body id="body" class="">
    <aside class="left-sidebar"></aside>
    <button id="sidebar-toggler" class="sidebar-toggle">Collapse/Expand</button>
</body>

Only a few adjustments are needed in the javascript:

$('document').ready(function() {

    if (typeof window.isMinified === "undefined") {
        window.isMinified = false;
    }

    const body = $('#body');
    $("#sidebar-toggler").on("click", function () {
        
        document.querySelector('.left-sidebar').style.transition = 'inherit';

        if (window.isMinified === false) {

            localStorage.setItem('menu-closed', !body.hasClass('sidebar-minified'));
            body.removeClass('sidebar-minified-out').addClass('sidebar-minified');
            window.isMinified = true;

        } else {

            localStorage.setItem('menu-closed', !body.hasClass('sidebar-minified'));
            body.removeClass('sidebar-minified').addClass('sidebar-minified-out');
            window.isMinified = false;

        }
            
    });

    const state = localStorage.getItem('menu-closed');

    if (state === null) {

        body.addClass('sidebar-minified');

    } else {

        const closed = state === "true" ? true : false;

        if (!closed) {
            body.addClass('sidebar-minified-out');
            document.querySelector('.left-sidebar').style.transition = 'none';
        }
        else {
            body.addClass('sidebar-minified');
        }

    }
});

Two significant changes to note in the above code:

// in the click event section: set the transition to default behavior
document.querySelector('.left-sidebar').style.transition = 'inherit';

Apply the correct class based on the state and disable the transition:

// ...

if (state === null) {

    body.addClass('sidebar-minified');

} else {

    // ...

    if (!closed) {
        body.addClass('sidebar-minified-out');
        document.querySelector('.left-sidebar').style.transition = 'none';
    }
    else {
        body.addClass('sidebar-minified');
    }

}

*** Update ***

The code has been refactored and optimized. Check out the fiddle for the updated version.

HTML:

<body id="body">
    <aside class="left-sidebar"></aside>
    <button id="sidebar-toggler" class="sidebar-toggle">Collapse/Expand</button>
</body>

CSS:

#body {
    background: #fff;
    transition: all .3s;
}
aside.left-sidebar {
    background-color: #2c0963;
    height: 100vh;
    width: 75px;
}
.sidebar-minified-out .left-sidebar {
    width: 180px;
}
.sidebar-transitions .left-sidebar {
    transition: width .3s ease-in; 
} 
.sidebar-toggle {
    font-weight: 300;
    font-size: 15px;
    cursor: pointer;
    height: 30px;
    position: absolute;
    left: 20%;
    top: 0;
}

JS:

$('document').ready(function() {

    $("#sidebar-toggler").on("click", function () {
        
        localStorage.setItem('menu-closed', $('#body').hasClass('sidebar-minified-out'));

        $('#body').addClass('sidebar-transitions').toggleClass('sidebar-minified-out');
            
    });

    localStorage.getItem('menu-closed') === "true" ? $('#body').removeClass('sidebar-minified-out') : $('#body').addClass('sidebar-minified-out');

});

Answer №4

Have you considered relocating the animation to a distinct class, such as

.sidebar-animated{
   transition: width: 0.3s ease-in;
}

and eliminating it from other locations, then applying that class using a timeout function so it is added after the transition completes? You can utilize useTimeout with a 0-second delay, like this:

setTimeout(() => {
    $('aside').addClass('sidebar-animated')    
},0)

Furthermore, CSS utilizes Specificity to target elements, so

.sidebar-minified-out .left-sidebar {
  width: 180px;
}

.sidebar-minified .left-sidebar {
  width: 75px;
} 

should be revised to:

.sidebar-minified .left-sidebar {
  width: 75px;
} 

.sidebar-minified-out .left-sidebar {
  width: 180px;
}

When both rules apply to the same element, the latter one will be used due to the same specificity rule. Give it a try as a last resort for me :D.

That should be sufficient to resolve the issue.

https://codepen.io/menawer_cpe/pen/qBZbEdw

Here is a functional demonstration. Please note: there may be a problem managing the state when the sidebar is initially collapsed, but this is related to how you handle the state.

Why useTimeout with a 0 delay? It ensures the execution is deferred to the "event loop," guaranteeing it runs after all regular JavaScript code is executed.

Answer №5

If you're looking for a solution, you can give this a try:

$('document').ready(function() {

  if (window.isMinified === undefined) {
    window.isMinified = false;
  }

  const body = $('#body');
  $("#sidebar-toggler").on("click", function() {
  
    $('#body .left-sidebar').removeAttr("style");

    if (window.isMinified === false) {
    
      body.removeClass("sidebar-minified-out").addClass("sidebar-minified");
      window.isMinified = true;

    } else {

      body.removeClass("sidebar-minified").addClass("sidebar-minified-out");
      window.isMinified = false;

    }

  });

  var firstTime = true;
  var sidebar = $('#body aside.left-sidebar');
  const state = !(null); //localStorage.getItem('menu-closed');

  if (state === null) {
    $(body).removeClass('sidebar-minified');

  } else {

    if (firstTime) {
      sidebar.css('transition', 'none');
      firstTime = false;
    }

    const closed = state === "true" ? true : false;

    if (!closed) {
      $(body).removeClass('sidebar-minified');
    }

  }
});
#body {
  background: #fff;
  transition: all 0.3s;
}

aside.left-sidebar {
  background-color: #2c0963;
  height: 100vh;
}

.sidebar-minified-out .left-sidebar {
  width: 180px;
  transition: width .3s ease-in;
}

.sidebar-minified .left-sidebar {
  width: 75px;
  transition: width .3s ease-in;
}

.sidebar-toggle {
  font-weight: 300;
  font-size: 15px;
  cursor: pointer;
  height: 30px;
  position: absolute;
  left: 20%;
  top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<body id="body" class="sidebar-minified sidebar-minified-out">
  <aside class="left-sidebar"></aside>

  <button id="sidebar-toggler" class="sidebar-toggle">Collapse/Expand</button>

</body>

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

Optimizing CSS for Landscape Letter Print Alignment

When attempting to print in letter size and landscape orientation, the two divs fail to align properly. However, when using "@size a4 landscape" for printing, the alignment issue is resolved. What could be missing from my print CSS that would allow them to ...

Enhancing the aesthetic appeal of a form

I have created a form in HTML that utilizes JavaScript to pull data from a database. I am looking to style the form, but I'm unsure of how to proceed. Below is the form along with some CSS code. How can I integrate the two together? <form id=" ...

Azure-Graph is reporting an error: 'Invalid or missing Access Token.'

In my Node.js project, I effortlessly integrate azure APIs. Logging in: const MsRest = require('ms-rest-azure'); MsRest.loginWithServicePrincipalSecret(keys.appId, keys.pass, keys.tenantId); Creating a resource group: const { ResourceManageme ...

Is there a way to ensure that my background color extends across the entire webpage background?

I'm curious about how to make sure that the background color of sections 1 and 2 fills the entire webpage, similar to this example. Currently, the background color only covers half of the webpage, but I want it to extend across the entire background o ...

How can a JQuery function be used with SVG to trigger a second animation immediately after the first animation is finished?

For the handwriting effect animation, I utilized the animation-delay technique by dividing the object wherever it intersected, drawing paths, and converting them into clipping masks. This involved breaking the letter "W" into four parts, creating different ...

Having trouble getting the navigation function to work correctly for my ReactJS image slider

I am looking to create a simple image slider that contains 3 images in an array. I want to be able to navigate through the slider using just one function that can move back and forth between images. If you click "next" on the last image, it should bring ...

The standard jQuery Mobile CSS styling does not seem to be working across various browsers, extensive testing has been conducted

I'm currently experimenting with jQuery Mobile to enhance my skills, but I'm facing issues with applying the basic CSS styling. I have included the css link from the jQuery Mobile website and ensured that I am connected to the internet. However, ...

Issue with ng-bind-html in TranslateJS causing problems

I have been working on implementing translation in a web application using angularJS and the angular-translate repository, which can be found at . As per the documentation, it is possible to use this repository to set text for a specific element in the HTM ...

The usage of CSS pseudo elements with the position absolute property

To allow for the opacity of an image to be changed without affecting the parent container's opacity, I added the image as a pseudo-element. The position of the pseudo-element is set to absolute and relative to the parent to position it inside the p ...

An AJAX event handling function returns a null value upon invocation

Recently, I've been working on a function named 'getAuthor' which includes an AJAX event. Here's the code snippet: function getAuthor(id){ $.get('http://www.connectnigeria.com/articles/wp-json/wp/v2/users/74',function(e){ ...

Easy Steps to Align a Rotated Table Header

I am looking to rotate the header table using CSS while keeping all text together. Here is my CSS code: th.rotate { height: 100px; white-space: nowrap; } th.rotate>div { transform: rotate(-90deg); } Here is how I have applied this CSS: ...

"Enhancing User Interaction: The Dynamic Trio of Ajax, Php, and MySql in Comment

I have successfully developed a basic commenting system, featuring a form with two fields: name and comment. Upon submitting the values, it utilizes Ajax to dynamically add the comment to the existing page without refreshing the entire content. My next ob ...

Techniques to dynamically insert database entries into my table using ajax

After acquiring the necessary information, I find myself faced with an empty table named categorytable. In order for the code below to function properly, I need to populate records in categoryList. What should I include in categoryList to retrieve data fro ...

Toggle visibility of div content when hovering over a link by leveraging the data attribute, with the div initially visible

I have a collection of links: <p><a href="#" class="floorplan initial" data-id="king"><strong>King</strong></a><br> 4 Bedrooms x 2.5 Bathrooms</p> <p><a href="#" class="floorplan" data-id="wood">< ...

Guide on utilizing the list of names in a POST request

<td class="widht200"> <input type="text" name="agg" size="2" disabled="disabled"/> </td><td class="widht200"> <input type="text" name="agg" size="2" disabled="disabled"/></td><td class="widht200"> <input type=" ...

Tips for enhancing a JSON array by including attributes through JavaScripts

I need help in dynamically constructing JSON using Javascript. { "Events": [{ "Name": "Code Change", "Enabled": "true", "Properties": [{ "Name": "url", "Value": "val" }] }], "Properti ...

Sorting arrays in JavaScript can become tricky when dealing with arrays that contain values from two different arrays

When working with two arrays in JavaScript that are received from PHP, I combine them into a single array. Each element in these arrays contains a created_at value (from Laravel), and I want to sort these elements by their created_at values. The issue ari ...

Transforming jQuery into Angular - Press Button to update choices in Dropdown List

I am looking to integrate some AngularJS features into a website that currently utilizes jQuery. Here is the issue I am facing: With jQuery, clicking a button triggers a dropdown item change. Please refer to the jsfiddle below for an example: $('# ...

AngularJS - development of a service entity

After deliberating between posting in the Angular mailing list or seeking assistance from the JavaScript community, I have decided that this is more of a JavaScript question. Hopefully, the knowledgeable individuals on Stack Overflow can provide a quicker ...

What distinguishes the built-in Head component in Next.js from using the head tag directly in JSX?

Hey everyone, I am hoping this question fits well within this community. I've been confused about how the built-in Head component in Next.js actually works. My assumption is that Next.js automatically generates metadata and then Head either replaces o ...