Search code examples
javascripthtmlcsscheckboxsubmenu

My submenus didn't place themselves as I want + can't close them with a checkbox


I'm new to coding, learning back-end actually, but want to make something beautiful nonetheless. (And sorry for any English mistakes)

Anyway, I make this:

HTML :

<header>
<nav>
    <a href="#" class="logo"><i class="fas fa-globe-asia fa-2x"></i><span>Title<br>Title</span></a>
    <input class="menu-btn" type="checkbox" id="menu-btn" />
    <label class="menu-icon" for="menu-btn"><span class="navicon"></span></label>
    <ul class="menu">
        <li><a href="index.php"><i class="fas fa-home mr-2"></i> Accueil</a></li>
        <li class="nav-expand"><a href="#" class="nav-link"><span><i class="fas fa-chalkboard-teacher mr-2"></i> Click here for submenu  </span></a>
            <ul class="nav-expand-content">
                <li><a href="">1</a></li>
                <li class="nav-expand"><a href="#" class="nav-link">2</a>
                    <ul class="nav-expand-content">
                        <li><a href="#">2.1</a></li>
                        <li><a href="#">2.2</a></li>
                    </ul>
                </li>
                <li class="nav-expand"><a href="#" class="nav-link">3</a>
                    <ul class="nav-expand-content">
                        <li><a href="#">3.1</a></li>
                        <li><a href="#">3.2</a></li>
                    </ul>
                </li>
                <li class="nav-expand"><a href="#" class="nav-link">4 : click here for another submenu</a>
                    <ul class="nav-expand-content">
                        <li><a href="#">4.1</a></li>
                        <li class="nav-expand"><a href="#" class="nav-link">Test lvl 3</a>
                            <ul class="nav-expand-content">
                            <li class="nav-expand"><a href="#" class="nav-link">Test lvl 4</a>
                                <ul class="nav-expand-content">
                                <li class="nav-expand"><a href="#">Test lvl 5</a></li>
                                </ul>
                            </li>
                            </ul>
                        </li>
                    </ul>
                </li>
                <li><a href="#">5</a></li>
                <li><a href="#">6</a></li>
                <li><a href="#">7</a></li>
                <li><a href="#">8</a></li>
            </ul>
        </li>   
    </ul>
</nav>

CSS :

    /* ---------------------------------------------------------------- 
                                ALL
---------------------------------------------------------------- */


*{
    margin:0;
    padding:0;
    box-sizing:border-box;
}



/* ---------------------------------------------------------------- 
                                HEADER
---------------------------------------------------------------- */

header {
    background-color: rgb(235, 235, 235);
    box-shadow: 1px 1px 4px 0 rgba(0,0,0,.5), inset 0px 5px #C44816;
    padding-top: 5px;
    position: fixed;
    width: 100%;
}

header ul {
    margin: 0;
    padding: 5px;
    list-style: none;
    overflow: hidden;
    background-color: rgb(235, 235, 235);
}

header li a {
    display: block;
    padding: 20px 20px;
    border-right: 1px solid rgb(225, 225, 225);
    border-left: 1px solid rgb(225, 225, 225);
    text-decoration: none;
    color: #333;
}

header li a:hover {
    background-color: rgb(225, 225, 225);
}

.logo {
    display: block;
    float: left;
    font-size: 20px;
    padding: 10px 20px;
    text-decoration: none;
    color: #333;
}

.logo span {
    vertical-align: bottom;
    display: inline-block;
}

header .menu { /* Objectif : cacher le menu par défaut */
    clear: both;
    max-height: 0;
    transition: max-height .2s ease-out;
  }


    /* ----------------- Hamburger menu-icon ----------------- */

header .menu-icon {
    cursor: pointer;
    float: right;
    padding: 28px 20px;
    position: relative;
    user-select: none; /* L'icone du menu ne peut pas être sélectionnée par l'utilisateur */
}

header .menu-icon .navicon { /* Barre du milieu qui disparait en transition */
    background: #333;
    display: block;
    height: 2px;
    width: 18px;
    position: relative;
    transition: background .2s ease-out;
}
  
header .menu-icon .navicon:before,
  header .menu-icon .navicon:after { /* Les deux autres barres */
    background: #333;
    content: '';
    display: block;
    height: 100%;
    width: 100%;
    position: absolute;
    transition: all .2s ease-out;

  }
  
header .menu-icon .navicon:before { /* Barre du bas */
    top: 5px;
}

header .menu-icon .navicon:after { /* Barre du haut */
    top: -5px;
}



    /* ----------------- Hamburger Menu button behavior ----------------- */
  
header .menu-btn {
    display: none;
}
  
header .menu-btn:checked ~ .menu {
    max-height: 100vh;
}
  
header .menu-btn:checked ~ .menu-icon .navicon { /* Barre du milieu qui disparait*/
    background: transparent;
}
  
header .menu-btn:checked ~ .menu-icon .navicon:before { /* Barre du bas qui tourne */
    transform: rotate(-45deg);
}
  
header .menu-btn:checked ~ .menu-icon .navicon:after { /* Barre du haut qui tourne */
    transform: rotate(45deg);
}
  
header .menu-btn:checked ~ .menu-icon:not(.steps) .navicon:before,
  header .menu-btn:checked ~ .menu-icon:not(.steps) .navicon:after { /* Les deux barres se croisent en leur milieu */
    top: 0;
  }

    /* ----------------- Sous-catégories ----------------- */
    
.nav-expand-content {
    position: absolute;
    top: 76px;
    left: 0;
    width: 100%;
    height: 100vh;
    overflow-y: auto;
    transform: translateX(100%);
    transition: 0.3s;
    visibility: hidden;
}

.active > .nav-expand-content {
    transform: translateX(0);
    visibility: visible;
    box-shadow: 1px 1px 4px 0 rgba(0,0,0,.5);
}

.nav-link {
    display: flex;
    justify-content: space-between;
}




    /* ----------------- Media Queries ----------------- */

@media (min-width: 768px) {
    header li {
        float: left;
    }
    header li a {
        padding: 20px 30px;
    }
    header .menu {
        clear: none;
        float: right;
        max-height: none;
    }
    header .menu-icon {
        display: none;
    }
}

JS :

    /* -------------------------------------
            Menu + sub-menu
--------------------------------------*/

let navExpand = document.querySelectorAll('.nav-expand')
let backLink = `<li class="nav-item">
    <a class="nav-back-link" href="javascript:;">
        <i class="fas fa-caret-left"></i> Back 
    </a>
</li>`
let forwardIcon = '<i class="fas fa-caret-right"></i>'

navExpand.forEach(item => {
    item.querySelector('.nav-expand-content').insertAdjacentHTML('afterbegin', backLink)
    item.querySelector('.nav-link').insertAdjacentHTML('beforeend', forwardIcon)
    item.querySelector('.nav-link').addEventListener('click', () => item.classList.add('active'))
    item.querySelector('.nav-back-link').addEventListener('click', () => item.classList.remove('active'))
})



    /* ----------- Close sub-menu when checkbox unchecked ----------- */

let checkBox = document.getElementById('menu-btn');

checkbox.addEventListener('change', e => {

    if(e.target.checked==false){
        navExpand.forEach(item => {
            item.classList.remove('active')
        })
    }
})

Knowing that right now, I'm working on a mobile-first, so, you'll need to resize to see my problems.

Problem 1 : CSS problem

The first level of submenu works as intended, above the original menu, and we can still see the titles and hamburger logo. But after this, the next level comes just under it, instead of over it, and so on. I don't know how to stack them without them having another position. If I put their top position to 0, they work as I intended them, but over ma titles and icon. So, I put 76px under this... I try a lot of different things, but these two are the nearest to what I want.

Someone advises me to put everything in another div, as a sidebar. I began to make this, but then, for a larger screen, it makes it looks awfully harder to do since then I'd have 2 navs working as one. It seems harder than to make one nav working as two for a mobile device, I guess? And I don't understand why it shouldn't be working if it's inside the same nav, (and I don't like it when I don't understand something) so I stopped. Even if as so, the side menu was working as intended. So, I guess if I can't work it out, I'll go there. But I'd love to avoid it.

Problem 2 : JS problem

As you can see, this makes a lot of submenus, and with the active class, we have to remove them one by one. I made some code to make it as if we unchecked the menu hamburger, they should all remove their active class, but whatever the code, it doesn't work, and I can't even begin to understand why.

Thanks for your time and advice in advance

PS: I don't know any JQuery yet '^^


Solution

  • CSS problem could make menu "position: relative;" them submenu use top:0 to cover each other.

    js when item.querySelector('XXX') find nothing can't "insertAdjacentHTML" or else would crash then get unknown mistake so try to avoid it.

    p.s. in js, checkBox and checkbox is different. :p

    search "check this" to check which line i modify.

        let navExpand = document.querySelectorAll('.nav-expand')
        let backLink = `<li class="nav-item">
        <a class="nav-back-link" href="javascript:;">
            <i class="fas fa-caret-left"></i> Back 
        </a>
    </li>`
        let forwardIcon = '<i class="fas fa-caret-right"></i>'
    
        navExpand.forEach(item => {  // check this
          if (item.querySelector('.nav-expand-content')) item.querySelector('.nav-expand-content').insertAdjacentHTML('afterbegin', backLink);
          if (item.querySelector('.nav-link')) item.querySelector('.nav-link').insertAdjacentHTML('beforeend', forwardIcon);
          if (item.querySelector('.nav-link')) item.querySelector('.nav-link').addEventListener('click', () => item.classList.add('active'));
          if (item.querySelector('.nav-back-link')) item.querySelector('.nav-back-link').addEventListener('click', () => item.classList.remove('active'));
        })
    
        // 
    
        /* ----------- Close sub-menu when checkbox unchecked ----------- */
    
        let checkBox = document.getElementById('menu-btn');
    
        checkBox.addEventListener('change', e => {   // check this
          if (e.target.checked == false) {
            let actives = document.querySelectorAll('.active');
            actives.forEach(item => {
              item.classList.remove('active')
            })
          }
        })
        /* ---------------------------------------------------------------- 
                                    ALL
    ---------------------------------------------------------------- */
    
    
        * {
          margin: 0;
          padding: 0;
          box-sizing: border-box;
        }
    
    
    
        /* ---------------------------------------------------------------- 
                                    HEADER
    ---------------------------------------------------------------- */
    
        header {
          background-color: rgb(235, 235, 235);
          box-shadow: 1px 1px 4px 0 rgba(0, 0, 0, .5), inset 0px 5px #C44816;
          padding-top: 5px;
          position: fixed;
          width: 100%;
        }
    
        header ul {
          margin: 0;
          padding: 5px;
          list-style: none;
          overflow: hidden;
          background-color: rgb(235, 235, 235);
        }
    
        header li a {
          display: block;
          padding: 20px 20px;
          border-right: 1px solid rgb(225, 225, 225);
          border-left: 1px solid rgb(225, 225, 225);
          text-decoration: none;
          color: #333;
        }
    
        header li a:hover {
          background-color: rgb(225, 225, 225);
        }
    
        .logo {
          display: block;
          float: left;
          font-size: 20px;
          padding: 10px 20px;
          text-decoration: none;
          color: #333;
        }
    
        .logo span {
          vertical-align: bottom;
          display: inline-block;
        }
    
        header .menu {
          /* check this */
          position: relative;
          /* Objectif : cacher le menu par défaut */
          clear: both;
          max-height: 0;
          transition: max-height .2s ease-out;
        }
    
    
        /* ----------------- Hamburger menu-icon ----------------- */
    
        header .menu-icon {
          cursor: pointer;
          float: right;
          padding: 28px 20px;
          position: relative;
          user-select: none;
          /* L'icone du menu ne peut pas être sélectionnée par l'utilisateur */
        }
    
        header .menu-icon .navicon {
          /* Barre du milieu qui disparait en transition */
          background: #333;
          display: block;
          height: 2px;
          width: 18px;
          position: relative;
          transition: background .2s ease-out;
        }
    
        header .menu-icon .navicon:before,
        header .menu-icon .navicon:after {
          /* Les deux autres barres */
          background: #333;
          content: '';
          display: block;
          height: 100%;
          width: 100%;
          position: absolute;
          transition: all .2s ease-out;
    
        }
    
        header .menu-icon .navicon:before {
          /* Barre du bas */
          top: 5px;
        }
    
        header .menu-icon .navicon:after {
          /* Barre du haut */
          top: -5px;
        }
    
    
    
        /* ----------------- Hamburger Menu button behavior ----------------- */
    
        header .menu-btn {
          display: none;
        }
    
        header .menu-btn:checked~.menu {
          max-height: 100vh;
          /* check this */
          overflow: visible;
        }
    
        header .menu-btn:checked~.menu-icon .navicon {
          /* Barre du milieu qui disparait*/
          background: transparent;
        }
    
        header .menu-btn:checked~.menu-icon .navicon:before {
          /* Barre du bas qui tourne */
          transform: rotate(-45deg);
        }
    
        header .menu-btn:checked~.menu-icon .navicon:after {
          /* Barre du haut qui tourne */
          transform: rotate(45deg);
        }
    
        header .menu-btn:checked~.menu-icon:not(.steps) .navicon:before,
        header .menu-btn:checked~.menu-icon:not(.steps) .navicon:after {
          /* Les deux barres se croisent en leur milieu */
          top: 0;
        }
    
        /* ----------------- Sous-catégories ----------------- */
    
        .nav-expand-content {
          /* check this */
          position: absolute;
          top: 0px;
          left: 0;
          width: 100%;
          height: 100vh;
          overflow-y: auto;
          transform: translateX(100%);
          transition: 0.3s;
          visibility: hidden;
        }
    
        .active>.nav-expand-content {
          transform: translateX(0);
          visibility: visible;
          box-shadow: 1px 1px 4px 0 rgba(0, 0, 0, .5);
        }
    
        .nav-link {
          display: flex;
          justify-content: space-between;
        }
    
    
    
    
        /* ----------------- Media Queries ----------------- */
    
        @media (min-width: 768px) {
          header li {
            float: left;
          }
    
          header li a {
            padding: 20px 30px;
          }
    
          header .menu {
    
            clear: none;
            float: right;
            max-height: none;
          }
    
    
          header .menu-icon {
            display: none;
          }
        }
      <header>
        <nav>
          <a href="#" class="logo"><i class="fas fa-globe-asia fa-2x"></i><span>Title<br>Title</span></a>
          <input class="menu-btn" type="checkbox" id="menu-btn" />
          <label class="menu-icon" for="menu-btn"><span class="navicon"></span></label>
          <ul class="menu">
            <li><a href="index.php"><i class="fas fa-home mr-2"></i> Accueil</a></li>
            <li class="nav-expand"><a href="#" class="nav-link"><span><i class="fas fa-chalkboard-teacher mr-2"></i> Click
                  here for submenu </span></a>
              <ul class="nav-expand-content">
                <li><a href="">1</a></li>
                <li class="nav-expand"><a href="#" class="nav-link">2</a>
                  <ul class="nav-expand-content">
                    <li><a href="#">2.1</a></li>
                    <li><a href="#">2.2</a></li>
                  </ul>
                </li>
                <li class="nav-expand"><a href="#" class="nav-link">3</a>
                  <ul class="nav-expand-content">
                    <li><a href="#">3.1</a></li>
                    <li><a href="#">3.2</a></li>
                  </ul>
                </li>
                <li class="nav-expand"><a href="#" class="nav-link">4 : click here for another submenu</a>
                  <ul class="nav-expand-content">
                    <li><a href="#">4.1</a></li>
                    <li class="nav-expand"><a href="#" class="nav-link">Test lvl 3</a>
                      <ul class="nav-expand-content">
                        <li class="nav-expand"><a href="#" class="nav-link">Test lvl 4</a>
                          <ul class="nav-expand-content">
                            <li class="nav-expand"><a href="#">Test lvl 5</a></li>
                          </ul>
                        </li>
                      </ul>
                    </li>
                  </ul>
                </li>
                <li><a href="#">5</a></li>
                <li><a href="#">6</a></li>
                <li><a href="#">7</a></li>
                <li><a href="#">8</a></li>
              </ul>
            </li>
          </ul>
        </nav>
      </header>