javascripthtmlsidebarscss-mixinssidenav

My Side navbar doesn't collapse after page reload


I have a side navbar using HTML, SCSS and js. When I click reload, it goes to uncollapse state.

HTML

<nav class="nav" id="myNav">
    <button class="nav__toggle" id="toggle-btn">
        <span class="hamburger"></span>
    </button>
    <ul>
        <li class="brand">
            <a href="">
                <img src="/images/logo.png" class="img-fluid" alt="NC Designs logo">
            </a>
        </li>
        <li><a href="#creative">My creatives</a></li>
        <li><a href="#about">About me</a></li>
        <li><a href="#portfolio">My Portfolio</a></li>
        <li><a href="#clients">Clients</a></li>
        <li><a href="#testimonials">Testimonials</a></li>
        <li><a href="#contact">Contact me</a></li>
    </ul>
</nav>

SCSS

.nav {
  position: absolute;
  text-align: center;
  right: 0;
  width: 260px;
  height: 100vh;
  background: var(--clr-light);
  box-shadow: 0 0 3em #00000026;
  transform: translateX(100%);
  transition: transform 300ms cubic-bezier(0.5, 0, 0.5, 1);

  ul {
    list-style: none;
    margin: 0;
    padding: 0;
    .li:nth-child(0){
      margin-bottom: 1rem!important;
    }

    li {
      margin-bottom: 2em;
      display: flex;
      
      a {
        text-decoration: none;
        color: var(--clr-dark);
        padding: .5em;
        flex: 1;
        &:hover {
          text-decoration: underline;
          color: var(--clr-primary);
        }
      }
    }
  }
}
.brand{
  margin-top: 2em;
}
.brand img{
  height:100px
}
.nav__toggle {
  position: absolute;
  top: 2em;
  left: 0;
  transform: translateX(-100%);
  background: var(--clr-light);
  padding: 1em 0.5em;
  border: 0;
  border-radius: 0.25em 0 0 0.25em;
  cursor: pointer;
  transition: left 600ms ease-in-out, padding 500ms,
    transform 3500ms ease-in-out, opacity 200ms linear;
  &:focus {
    outline: 0;
    box-shadow: 0 0 0 1px rgba(238, 99, 82, 0.5);
  }
}

.hamburger {
  display: block;
  position: relative;
  &::before,
  &::after {
    content: "";
    position: absolute;
    left: 0;
  }
  &::before {
    bottom: 6px;
  }
  &::after {
    top: 6px;
  }
}

.hamburger,
.hamburger::before,
.hamburger::after {
  width: 2em;
  height: 3px;
  background: var(--clr-dark);
  transition: transform 350ms ease-in-out, opacity 200ms linear;
}

/* Navigation open styles */
.nav-open {
  .nav {
    -webkit-transform: translateX(0);
    transform: translateX(0);
  }
  /* Change this stuff below */
  .hamburger {
    -webkit-transform: rotate(45deg) scale(0.7);
    transform: rotate(45deg) scale(0.7);
    &::before {
      opacity: 0;
    }
    &::after {
      transform: rotate(90deg) translate(-6px) scale(1);
    }
  }
  .nav__toggle {
    position: absolute;
    top: 2em;
    left: 98%;
    transform: translateX(-100%);
    background: var(--clr-light);
    padding: 1em 0.1em;
    border: 0;
    border-radius: 50%;
    box-shadow: 0 0 0.5em rgba(0, 0, 0, 0.2);
    margin-bottom: 50px;
    &:focus {
      outline: 0;
      border-radius: 50%;
      box-shadow: 0 0 0 1px rgba(238, 99, 82, 0.25), 0 0 0.5em rgba(0, 0, 0, 0.25);
    }
    &:hover {
      outline: 0;
      border-radius: 50%;
      box-shadow: 0 0 0.5em rgba(0, 0, 0, 0.55);
    }
  }
}

JS

<script>
    const navToggle = document.querySelector('.nav__toggle');
    const navFocus = document.querySelector('nav ul li a');

    navToggle.addEventListener('click', () => {
        document.body.classList.toggle('nav-open');
    });

    /* 
       Allow the toggle to work if the tab key is used to access menu...
       I'm not sure if this is the best way or if it works all the time.
       I tried experimenting with keyup and the keycode but this seemed simple.
    */
    navFocus.addEventListener('focus', () => {
        document.body.classList.toggle('nav-open');
    });
</script>

repo link

Note: when I click the refresh second time, the navbar opens (uncollapsed), that should not happen (should remain collapsed even if I reload from the uncollapsed state (open navbar state). What should I do?


Solution

  • This behavior is expected if you were to reload the page. However, if you wanted to override this, you need a way to save the state between page refreshes. I think you should take a look at the sessionStorage API: https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage

    A couple of things to point you in the right direction: When you load the page, you should access the state of the sidebar. I think opened or closed would be enough to remember the state of the sidebar. Something like:

    const navBarKey = "navBarState"; // using variable to prevent typos
    const navBarState = sessionStorage.getItem(navBarKey);
    const open = "open"; // using variable to prevent typos
    const navOpen = 'nav-open'; // using variable to prevent css class typos
    
    if(navBarState === open){
     // this code will run when first started. If not, you may need to add some window.onload logic. 
     // if the saved state is open, add 'nav-open'
     body.classList.add(navOpen);
    }
    
    const navToggle = document.querySelector('.nav__toggle');
    navToggle.addEventListener('click', () => {
     
     if(body.classList.contains(navOpen)) {
       // if already open, remove 'nav-open' and remove 'navBarState' from sessionStorage
       body.classList.remove(navOpen);
       sessionStorage.removeItem(navBarKey)
     } else {
       // if not open, add 'mav-open' and add 'navBarState' to sessionStorage
       body.classList.add(navOpen);
       sessionStorage.setItem(navBarKey, open)
    
     }
    });        
    
    

    sessionStorage will persist only while the window/tab is open, and will clear if you exit the browser. localStorage, however, will persist even after the brower is closed. So, if you wanted to persist after restarting the browser, you should instead use localStorage. https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage

    I hope this is enough for you to get started! There are of course, different ways to handle this/refactoring opportunities, but for the given code you have at hand, this should suffice.