Search code examples
javascripthtmlcss

dropdown menu does not fit in the container when opened


I have got follow html:

nav {
  width: 100px;
}


.nav-menu {
  background-color: orange;
}

.dropdown {
  position: relative;
}


a[data-toggle="dropdown"] {
  display: flex;
  align-items: center;
  background: transparent;
  color: blue;
  padding: 10px 15px;
  border-radius: 4px;
  transition: background 0.3s ease;
  white-space: nowrap;
}

a[data-toggle="dropdown"]:hover {
  color: black;
}

.nav-menu {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  list-style-type: none;
  padding: 0;
  margin: 0;
  align-items: center;
  width: 100%;

}

.dropdown-menu {
  list-style: none;
  padding: 10px 15px;
  margin: 0;
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 200px;
  background: #ffffff;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
  border-radius: 6px;
  overflow: hidden;
  transform: scaleY(0);
  transform-origin: top;
  opacity: 0;
  transition: transform 0.3s ease, opacity 0.3s ease;
  z-index: 1000;
}

.dropdown-menu.show {
  transform: scaleY(1);
  opacity: 1;
}

.dropdown-menu:before {
  display: block;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  clip-path: inset(0 0 0 0);
}
<nav>
  <ul class="nav-menu">

    <li class="dropdown">
      <a href="#" data-toggle="dropdown">About</a>
      <ul class="dropdown-menu">
        <li><a href="/foo.html">Foo</a></li>
        <li><a href="/bar.html">Bar</a></li>
        <li><a href="/bar.html">Some long text here</a></li>
      </ul>
    </li>

  </ul>
</nav>

But I want to make menu be shown no wider than the dimensions of the container (orange color). The menu must always fit into the container.

menu tears apart the container

https://jsfiddle.net/Suliman123/8tg67sq4/

If menu is longer that container text menu it's text should be fully displayed. In my case there is a lot of menu items. And if no space from right it always have space from left.

Here is illustration: https://jsfiddle.net/Suliman123/Lno7a5p0/ menu here should not be bigger than black region.


Solution

  • We use offsetWidth property in JS to calculate the width of the button - then the event listener in js will apply this width to the dropdown using style.width property.

    You need to make these changes in CSS in the .dropdown-menu:

    width: auto;
    min-width: unset;
    

    This will remove any preset width or min-width in the CSS and will let the JS add on to your CSS.

    This will be your updated JS:

    document.querySelectorAll('.dropdown').forEach((dropdown) => {
      const button = dropdown.querySelector('a[data-toggle="dropdown"]');
      const menu = dropdown.querySelector('.dropdown-menu');
    
      button.addEventListener('click', (event) => {
        event.preventDefault();
        document.querySelectorAll('.dropdown-menu.show').forEach((openMenu) => {
          if (openMenu !== menu) {
            openMenu.classList.remove('show');
          }
        });
        const buttonWidth = button.offsetWidth;
        menu.style.width = `${buttonWidth}px`;
        menu.classList.toggle('show');
      });
    });
    
    // to close the dropdown when clicked anywhere else on the page
    document.addEventListener('click', (event) => {
      if (!event.target.closest('.dropdown')) {
        document.querySelectorAll('.dropdown-menu.show').forEach((menu) => {
          menu.classList.remove('show');
        });
      }
    });

    Your complete HTML, CSS and JS would become:

    document.querySelectorAll('.dropdown').forEach((dropdown) => {
      const button = dropdown.querySelector('a[data-toggle="dropdown"]');
      const menu = dropdown.querySelector('.dropdown-menu');
    
      button.addEventListener('click', (event) => {
        event.preventDefault();
    
        document.querySelectorAll('.dropdown-menu.show').forEach((openMenu) => {
          if (openMenu !== menu) {
            openMenu.classList.remove('show');
          }
        });
    
        const buttonWidth = button.offsetWidth;
        menu.style.width = `${buttonWidth}px`;
    
        // Toggle the visibility of the dropdown menu
        menu.classList.toggle('show');
      });
    });
    
    document.addEventListener('click', (event) => {
      if (!event.target.closest('.dropdown')) {
        document.querySelectorAll('.dropdown-menu.show').forEach((menu) => {
          menu.classList.remove('show');
        });
      }
    });
     nav {
       width: 100px;  
     }
       
    
    .nav-menu {
       background-color: orange;
     }
     
     .dropdown {
        position: relative;
      }
       
      
      a[data-toggle="dropdown"] {
        display: flex;
        align-items: center;
        background: transparent;
        color: blue;
        padding: 10px 15px;
        border-radius: 4px;
        transition: background 0.3s ease;
        white-space: nowrap;
      }
      
      a[data-toggle="dropdown"]:hover {
        color: black;
      }
    
    .nav-menu {
        display: flex;
        flex-wrap: wrap; 
        gap: 10px;
        list-style-type: none;
        padding: 0;
        margin: 0;
        align-items: center;
        width: 100%;
        
    }
    
    
      .dropdown-menu {
      list-style: none;
      padding: 10px 15px;
      margin: 0;
      position: absolute;
      top: 100%;
      left: 0;
      background: #ffffff;
      box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
      border-radius: 6px;
      overflow: hidden;
      transform: scaleY(0);
      transform-origin: top;
      opacity: 0;
      transition: transform 0.3s ease, opacity 0.3s ease;
      z-index: 1000;
    
      /* just need to add this - this will remove any preset width or min-width */
      width: auto;
      min-width: unset;
    }
      
      
        
      .dropdown-menu.show {
        transform: scaleY(1);
        opacity: 1;
      }
      
      .dropdown-menu:before {
        /* content: ""; */
        display: block;
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        clip-path: inset(0 0 0 0);
      }
    <!-- UNCHANGED - NOTHING NEEDS TO BE CHANGED HERE IN HTML -->
    <nav>
        <ul class="nav-menu">
    
            <li class="dropdown">
                <a href="#" data-toggle="dropdown">About</a>
                <ul class="dropdown-menu">
                  <li><a href="/foo.html">Foo</a></li>
                  <li><a href="/bar.html">Bar</a></li>
                </ul>
           </li>
    
        </ul>
    </nav>