skip to Main Content

I have build an offcnavas menu with 3 category levels which are nested within the HTML markup. Category level1 opens category level2 which opens the level 3. Here is the snippet:

const items = document.querySelectorAll('.navigation-offcanvas-list-item');

items.forEach(item => {
  item.addEventListener('click', event => {
    const containsIsOpen = item.classList.contains("is-open")

    if (!containsIsOpen) {
      item.classList.add("is-open")
    } else {
      item.classList.remove("is-open")
    }
  })
})
.third .navigation-offcanvas-list-item {
     overflow: hidden;
     transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
     max-height: 0;
     transform: scaleY(0);
     transform-origin: top;
}
 .second {
     overflow: hidden;
     transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
     max-height: 0;
     transform: scaleY(0);
     transform-origin: top;
}
 .second .navigation-offcanvas-list-item.is-open .third .navigation-offcanvas-list-item {
     max-height: none;
     transform: scaleY(1);
}
 .first .navigation-offcanvas-list-item.is-open .second {
     max-height: none;
     transform: scaleY(1);
}
 
<div class="navigation-offcanvas-container js-navigation-offcanvas">
  <div class="navigation-offcanvas-overlay-content">
    <!-- FIRST LEVEL -->
    <ul class="list-unstyled navigation-offcanvas-list">
      <li>
        <div class="first">
          <ul>
            <li class="navigation-offcanvas-list-item" data-first-trigger="id1" data-offcanvas-menu-level="level1">
              <div class="navigation-offcanvas-link nav-item">
                <span class="navigation-offcanvas-link-icon js-navigation-offcanvas-loading-icon">Category LEVEL1</span>
              </div>
              <div class="second" data-first-offcanvas="id1">
                <ul>
                  <li class="navigation-offcanvas-list-item" data-second-trigger="idd1" data-offcanvas-menu-level="level2">
                    <div class="navigation-offcanvas-link nav-item">
                      <span class="navigation-offcanvas-link-icon js-navigation-offcanvas-loading-icon">Category1 LEVEL2</span>
                    </div>
                    <div class="third" data-second-offcanvas="idd1">
                      <ul>
                        <li class="navigation-offcanvas-list-item">final link</li>
                        <li class="navigation-offcanvas-list-item">final link</li>
                        <li class="navigation-offcanvas-list-item">final link</li>
                      </ul>
                    </div>
                  </li>
                  <li class="navigation-offcanvas-list-item" data-second-trigger="idd2" data-offcanvas-menu-level="level2">
                    <div class="navigation-offcanvas-link nav-item">
                      <span class="navigation-offcanvas-link-icon js-navigation-offcanvas-loading-icon">Category2 LEVEL2</span>
                    </div>
                    <div class="third" data-second-offcanvas="idd2">
                      <ul>
                        <li class="navigation-offcanvas-list-item">final link</li>
                        <li class="navigation-offcanvas-list-item">final link</li>
                        <li class="navigation-offcanvas-list-item">final link</li>
                      </ul>
                    </div>
                  </li>
                </ul>
              </div>
            </li>
          </ul>
        </div>
      </li>
    </ul>
  </div>
</div>

Now the problem is, if I open the first level the class is-open is getting added correctly. The same is true for level2. The submenu opens correctly.

But now I need to close the second submenu when clicking the second level link, as well as the first submenu when clicking the first level link. The problem is (I guess, but I’m not sure), the HTML markup is so nested, that when I click on the second level link to open the second submenu, it will remove is-open from the entire markup. You can see the behaviour in the snippet.

How would I open and close the submenus correctly?

2

Answers


  1. Use this:

    event.stopPropagation();
    

    event.stopPropagation()

    Stops the bubbling of an event to parent elements, preventing any parent handlers from being notified of the same event.

    classList.toggle(classname)
    

    Removes an existing class name from the list and returns false. If the class name doesn’t exist it’s added and the function returns true

    const items = document.querySelectorAll('.navigation-offcanvas-list-item');
    
    items.forEach(item => {
      item.addEventListener('click', event => {
        event.stopPropagation();// add this
        item.classList.toggle("is-open")// use toogle
        const nesteditems = item.querySelectorAll('.navigation-offcanvas-list-item.is-open');
          nesteditems.forEach(element => {
            element.classList.remove('is-open');
          });
      })
    })
    .third .navigation-offcanvas-list-item {
         overflow: hidden;
         transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
         max-height: 0;
         transform: scaleY(0);
         transform-origin: top;
    }
     .second {
         overflow: hidden;
         transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
         max-height: 0;
         transform: scaleY(0);
         transform-origin: top;
    }
     .second .navigation-offcanvas-list-item.is-open .third .navigation-offcanvas-list-item {
         max-height: none;
         transform: scaleY(1);
    }
     .first .navigation-offcanvas-list-item.is-open .second {
         max-height: none;
         transform: scaleY(1);
    }
    <div class="navigation-offcanvas-container js-navigation-offcanvas">
      <div class="navigation-offcanvas-overlay-content">
        <!-- FIRST LEVEL -->
        <ul class="list-unstyled navigation-offcanvas-list">
          <li>
            <div class="first">
              <ul>
                <li class="navigation-offcanvas-list-item" data-first-trigger="id1" data-offcanvas-menu-level="level1">
                  <div class="navigation-offcanvas-link nav-item">
                    <span class="navigation-offcanvas-link-icon js-navigation-offcanvas-loading-icon">Category LEVEL1</span>
                  </div>
                  <div class="second" data-first-offcanvas="id1">
                    <ul>
                      <li class="navigation-offcanvas-list-item" data-second-trigger="idd1" data-offcanvas-menu-level="level2">
                        <div class="navigation-offcanvas-link nav-item">
                          <span class="navigation-offcanvas-link-icon js-navigation-offcanvas-loading-icon">Category1 LEVEL2</span>
                        </div>
                        <div class="third" data-second-offcanvas="idd1">
                          <ul>
                            <li class="navigation-offcanvas-list-item">final link</li>
                            <li class="navigation-offcanvas-list-item">final link</li>
                            <li class="navigation-offcanvas-list-item">final link</li>
                          </ul>
                        </div>
                      </li>
                      <li class="navigation-offcanvas-list-item" data-second-trigger="idd2" data-offcanvas-menu-level="level2">
                        <div class="navigation-offcanvas-link nav-item">
                          <span class="navigation-offcanvas-link-icon js-navigation-offcanvas-loading-icon">Category2 LEVEL2</span>
                        </div>
                        <div class="third" data-second-offcanvas="idd2">
                          <ul>
                            <li class="navigation-offcanvas-list-item">final link</li>
                            <li class="navigation-offcanvas-list-item">final link</li>
                            <li class="navigation-offcanvas-list-item">final link</li>
                          </ul>
                        </div>
                      </li>
                    </ul>
                  </div>
                </li>
              </ul>
            </div>
          </li>
        </ul>
      </div>
    </div>
    Login or Signup to reply.
  2. const items = document.querySelectorAll('.navigation-offcanvas-list-item');
    
    items.forEach(item => {
      item.addEventListener('click', event => {
        let clickedElement = event.target;
    
        // eslint-disable-next-line no-cond-assign
        while (clickedElement && !clickedElement.classList.contains('navigation-offcanvas-list-item')) {
          clickedElement = clickedElement.parentNode;
        }
    
        if (clickedElement.classList.contains('navigation-offcanvas-list-item')) {
          let containsIsOpen = clickedElement.classList.contains("is-open");
          clickedElement.classList.toggle("is-open");
    
          if (containsIsOpen) {
            const nesteditems = clickedElement.querySelectorAll('.navigation-offcanvas-list-item.is-open');
            nesteditems.forEach(element => {
              element.classList.remove('is-open');
            });
          }
        }
      })
    })
    
    const secondItems = document.querySelectorAll('.second .navigation-offcanvas-list-item');
    
    secondItems.forEach(item => {
      item.addEventListener('click', event => {
        const containsIsOpen = item.classList.contains("is-open")
    
        if (!containsIsOpen) {
          item.classList.add("is-open");
        } else {
          item.classList.remove("is-open");
        }
      })
    })
    .third .navigation-offcanvas-list-item {
         overflow: hidden;
         transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
         max-height: 0;
         transform: scaleY(0);
         transform-origin: top;
    }
     .second {
         overflow: hidden;
         transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
         max-height: 0;
         transform: scaleY(0);
         transform-origin: top;
    }
     .second .navigation-offcanvas-list-item.is-open .third .navigation-offcanvas-list-item {
         max-height: none;
         transform: scaleY(1);
    }
     .first .navigation-offcanvas-list-item.is-open .second {
         max-height: none;
         transform: scaleY(1);
    }
    <div class="navigation-offcanvas-container js-navigation-offcanvas">
      <div class="navigation-offcanvas-overlay-content">
        <!-- FIRST LEVEL -->
        <ul class="list-unstyled navigation-offcanvas-list">
          <li>
            <div class="first">
              <ul>
                <li class="navigation-offcanvas-list-item" data-first-trigger="id1" data-offcanvas-menu-level="level1">
                  <div class="navigation-offcanvas-link nav-item">
                    <span class="navigation-offcanvas-link-icon js-navigation-offcanvas-loading-icon">Category LEVEL1</span>
                  </div>
                  <div class="second" data-first-offcanvas="id1">
                    <ul>
                      <li class="navigation-offcanvas-list-item" data-second-trigger="idd1" data-offcanvas-menu-level="level2">
                        <div class="navigation-offcanvas-link nav-item">
                          <span class="navigation-offcanvas-link-icon js-navigation-offcanvas-loading-icon">Category1 LEVEL2</span>
                        </div>
                        <div class="third" data-second-offcanvas="idd1">
                          <ul>
                            <li class="navigation-offcanvas-list-item">final link</li>
                            <li class="navigation-offcanvas-list-item">final link</li>
                            <li class="navigation-offcanvas-list-item">final link</li>
                          </ul>
                        </div>
                      </li>
                      <li class="navigation-offcanvas-list-item" data-second-trigger="idd2" data-offcanvas-menu-level="level2">
                        <div class="navigation-offcanvas-link nav-item">
                          <span class="navigation-offcanvas-link-icon js-navigation-offcanvas-loading-icon">Category2 LEVEL2</span>
                        </div>
                        <div class="third" data-second-offcanvas="idd2">
                          <ul>
                            <li class="navigation-offcanvas-list-item">final link</li>
                            <li class="navigation-offcanvas-list-item">final link</li>
                            <li class="navigation-offcanvas-list-item">final link</li>
                          </ul>
                        </div>
                      </li>
                    </ul>
                  </div>
                </li>
              </ul>
            </div>
          </li>
        </ul>
      </div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search