使用纯JS实现的多级动画下拉菜单的侧边栏

3

赏金任务

我在谷歌上搜索了类似的库,但没有找到...如果有人知道,请回答(只接受原生js库和MIT许可证)...

否则,请勿更改HTML结构...修复我的代码对我来说会更好..否则你可以用自己的方式解决问题..

结束赏金任务

const dataKeys = document.querySelectorAll('nav ul')
dataKeys.forEach(function(el,key){
    el.setAttribute('data-key',key);
})


const lists = document.querySelectorAll('ul ul')
lists.forEach(function(el){
    let sh = el.scrollHeight;
    el.setAttribute('data-sh',sh);
    el.classList.add('sub-menu')
    el.style.maxHeight =  0 + "px"; 
})
document.querySelectorAll("ul li").forEach(el => el.addEventListener('click', function(e){
    e.preventDefault();
    e.stopPropagation();
    try {
      let el = e.target.parentElement.children[1]; 
      let ul = e.target.parentElement.closest('ul');
      if(ul){
        ul.querySelectorAll('ul').forEach(function(item){
          item.style.minHeight = 0 + 'px';
        })
      }
      if(parseInt(getComputedStyle(el).minHeight) > 0){
        el.style.minHeight = 0 + "px";
      }else{
        el.style.minHeight =  el.scrollHeight + "px";
      }
    } catch (error) {
      return false;
    }
     return false;
}));



  document.querySelectorAll("ul ul li").forEach(el => el.addEventListener('click', function(e){

    e.preventDefault();
    e.stopPropagation();

    // const lists = document.querySelectorAll('ul ul')
    // lists.forEach(function(el){
      
    //     el.style.minHeight =  0 + "px"; 
    // })

    let el = null;
    let sh = 0;

     if(e.target.parentElement.hasChildNodes()){
      let ul = e.target.parentElement.querySelector('ul');
      if(e.target.parentElement.contains(ul)){
        el = e.target.parentElement.children[1];
        console.log(e.target.parentElement);
        console.log(e.target.parentElement.children[1]);
        sh  = el.scrollHeight;
      }
    }
      
    
      let elKey = parseInt(el.getAttribute('data-key'));

      let elli = e.target.parentElement.parentElement;

      let elH =  parseInt(el.getAttribute('data-sh'));
      let elliH =  parseInt(elli.getAttribute('data-sh'));

      // elli.style.minHeight = 0 + 'px';
      
      let elFirstChild = 0;
      if(el.hasChildNodes()){
        elFirstChild = el.children[0].scrollHeight;
        elFirstChild = parseInt(elFirstChild);
      }
    
      elli.style.minHeight = elliH + elFirstChild - elH + 'px';
      el.style.minHeight = elH + 'px';

     return false;
  }));
a {
    display: inline-block;
    width: 100%;
    height: 100%;
    padding: 5px 10px;

    color: white;
}

nav ul,
nav ul ul {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

nav ul {
    width: 200px;
    background: dodgerblue;
    position: relative;
}


nav ul ul {

    background: rgb(13, 130, 141);
    margin-left: 15px;
    border-left: 1px dashed white;
    transition: min-height .5s ease-in-out;

}

nav ul ul ul {
    background: rgb(1, 8, 8);
    margin-left: 15px;
    border-left: 1px dashed white;

}

nav ul ul ul ul {
    background: rgb(10, 41, 179);
    margin-left: 15px;
    border-left: 1px dashed white;
}
  <nav>
        <ul>
            <li><a href="javascript:void(0)">Home</a></li>
            <li><a href="javascript:void(0)">Dropdown 1 </a>
                <ul class="label-one">
                    <li><a href="javascript:void(0)"> Menu 1</a></li>
                    <li><a href="javascript:void(0)">Menu 2</a>
                        <ul class="label-two">
                            <li><a href="javascript:void(0)"> 1 Sub Menu 1</a></li>
                            <li><a href="javascript:void(0)"> 1 Sub Menu 2</a></li>
                            <li><a href="javascript:void(0)"> 1 Sub Menu 3</a> </li>
                        </ul>
                    </li>
                    <li><a href="javascript:void(0)">Menu 3</a>
                        <ul class="label-two">
                            <li><a href="javascript:void(0)"> 1 Sub Menu 1</a></li>
                            <li><a href="javascript:void(0)"> 1 Sub Menu 2</a></li>
                            <li><a href="javascript:void(0)"> 1 Sub Menu 3</a> </li>
                            <li><a href="javascript:void(0)"> 1 Sub Menu 3</a> </li>
                            <li><a href="javascript:void(0)"> 1 Sub Menu 3</a> </li>
                            <li><a href="javascript:void(0)"> 1 Sub Menu 3</a> </li>
                            <li><a href="javascript:void(0)"> 1 Sub Menu 3</a> </li>
                        </ul>
                    </li>
                    <li><a href="javascript:void(0)">Menu 4</a>
                        <ul class="label-two">
                            <li><a href="javascript:void(0)"> 1 Sub Menu 1</a></li>
                            <li><a href="javascript:void(0)"> 1 Sub Menu 2</a></li>
                            <!-- <li><a href="javascript:void(0)"> 1 Sub Menu 3</a> </li> -->
                        </ul>
                    </li>
                </ul>
            </li>
            <li><a href="javascript:void(0)">Dropdown 2 </a>
                <ul class="label-one" >
                    <li><a href="javascript:void(0)">Menu 2 </a>
                        <ul >
                            <li><a href="javascript:void(0)"> 2 Sub Menu 1</a></li>
                            <li><a href="javascript:void(0)"> 2 Sub Menu 2</a></li>
                        </ul>
                    </li>
                </ul>
            </li>
        </ul>
    </nav>


这个对你有用吗? - ac_mmi
@Ac_mmi 这不是我要找的... - نور
你正在寻找的是什么? - ac_mmi
我尚未在下拉菜单1中添加任何子菜单,但我已向菜单2和3中添加了子菜单。你可以只看到我的输出gif,其中可展开的只有那些子菜单。我并没有为所有按钮添加子菜单。 - ac_mmi
@Ac_mmi 首先点击 菜单2,然后再点击 菜单1,你会发现 菜单2 不会关闭或切换... - نور
显示剩余11条评论
2个回答

4

目前我能做的是这样的,如果对您有用,我可以更整洁地呈现它(我会尝试)

在这里,我创建了一个类为drop的div,在其中可以添加标签

输入图像描述

function onLoad() {

  var drop = document.getElementsByClassName('drop');
  var adj_flag = 0;
  var Height = 0;
  var array = [];
  var c = [];
  var a = document.getElementsByTagName('a');
  var d;


  for (var i = 0; i < a.length; i++) {
    if (a[i].nextElementSibling != "DIV") {
      a[i].onclick = function() {
        var p = this.parentElement.children;
        for (var j = 0; j < p.length; j++) {
          if (p[j].nodeName == "DIV") {
            var decre = style_length(p[j].style.height);

            if (decre > 0) {
              p[j].style.height = "0px";
              var div = p[j];
              while (div.parentElement.nodeName == "DIV") {

                div = div.parentElement;

                div.style.height = (style_length(div.style.height) - decre) + "px";

              }
              var query_div = p[j].querySelectorAll("*");
              for (var k = 0; k < query_div.length; k++) {
                if (query_div[k].nodeName == "DIV") {
                  if (style_length(query_div[k].style.height) > 0) {
                    query_div[k].style.height = "0px";
                  }
                }
              }

            }
          }
        }
      }
    }
  }

  for (var i = 0; i < drop.length; i++) {

    drop[i].style.height = '0px';
  }

  for (var i = 0; i < drop.length; i++) {



    drop[i].previousElementSibling.onclick = function() {
      if (this.nextElementSibling.nodeName == "DIV") {
        var elem = this.nextElementSibling;

        var height_div = 0;
        if (elem.style.height == "0px") {
          var check = elem.parentElement.children;

          for (var i = 0; i < check.length; i++) {
            if (check[i].nodeName == "DIV" && check[i] != elem) {
              if (check[i].style.height != "0px") {

                c.push(check[i].previousElementSibling);


              }
            }
          }


          var height_calc = elem.children;
          for (var i = 0; i < height_calc.length; i++) {
            if (height_calc[i].nodeName == "A") {
              height_div += 38;
            }
          }


          elem.style.height = height_div + 'px';
          var flag = 0;
          var add_h = height_div;
          var changes;
          var q;
          if (c.length > 0) {

            for (var i = 0; i < c.length; i++) {
              if (style_length(c[i].nextElementSibling.style.height) != 0) {
                if (style_length(elem.style.height) == style_length(c[i].nextElementSibling.style.height)) {
                  changes = 0;
                } else {
                  if (style_length(elem.style.height) < style_length(c[i].nextElementSibling.style.height)) {
                    add_h -= style_length(c[i].nextElementSibling.style.height);
                  } else {
                    add_h -= style_length(c[i].nextElementSibling.style.height);
                  }

                }
              }
              c[i].nextElementSibling.style.height = "0px";
              q = c[i].nextElementSibling.querySelectorAll("*");
              for (var k = 0; k < q.length; k++) {
                if (q[k].nodeName == "DIV") {
                  q[k].style.height = "0px";
                }
              }
            }
          }

          while (elem.parentElement.nodeName == "DIV") {
            elem = elem.parentElement;
            var h = elem.offsetHeight + add_h;
            elem.style.height = h + 'px';

          }

          c.pop();

          flag = 0;
        } else {
          var query = elem.querySelectorAll('*');


          var sub_h = elem.clientHeight;
          elem.style.height = "0px";
          while (elem.parentElement.nodeName == "DIV") {
            elem = elem.parentElement;
            var h = elem.clientHeight - sub_h;
            elem.style.height = h + 'px';
          }

          for (var i = 0; i < query.length; i++) {
            if (query[i].nodeName == "DIV") {


              query[i].style.height = "0px";
            }
          }


        }
      }
    }
  }

  function style_length(A) {
    A = A.substring(0, A.length - 2);
    var s = parseInt(A);
    return s;
  }

}
* {
  margin: 0px;
  padding: 0px;
  color: white;
  font-family: 'arial';
}

.menu {
  position: absolute;
  margin-left: 100px;
  display: flex;
  flex-direction: column;
  width: 250px;
  min-height: 114px;
  overflow: hidden;
  background-color: black;
}

.menu a {
  background-color: black;
  text-decoration: none;
  color: white;
  padding: 10px;
}

.drop {
  background-color: black;
  transition: height 0.4s;
  display: flex;
  flex-direction: column;
  margin-left: 15px;
  height: 0px;
}
<body onload="onLoad()">
  <nav class="menu">
    <a href="#/">HOME</a>
    <a href="#/">DropDown-1</a>
    <div class="drop">
      <a href="#/" style="background-color: #00175A;">MENU-1</a>

      <a href="#/" style="background-color: #00175A;">MENU-2</a>
      <div class="drop">
        <a href="#/" style="background-color: #030E2E;">SUBMENU-1</a>
        <a href="#/" style="background-color: #030E2E;">SUBMENU-2</a>
        <div class="drop">
          <a href="#/" style="background-color:grey;">submenu-1</a>
          <a href="#/" style="background-color:grey;">submenu-2</a>
          <a href="#/" style="background-color:grey;">submenu-3</a>
        </div>
        <a href="#/" style="background-color: #030E2E;">SUBMENU-3</a>
        <div class="drop">
          <a href="#/" style="background-color:grey;">submenu-1</a>
          <a href="#/" style="background-color:grey;">submenu-2</a>
        </div>

      </div>
      <a href="#/" style="background-color: #00175A;">MENU-3</a>
      <div class="drop">
        <a href="#/" style="background-color: #030E2E;">SUBMENU-1</a>
        <a href="#/" style="background-color: #030E2E;">SUBMENU-2</a>
      </div>

    </div>
    <a href="#/">DropDown-2</a>
    <div class="drop">
      <a href="#/" style="background-color: #00175A;">MENU-2</a>
      <div class="drop">
        <a href="#/" style="background-color: #030E2E;">SUBMENU-1</a>
        <a href="#/" style="background-color: #030E2E;">SUBMENU-2</a>
      </div>

    </div>

  </nav>
</body>


你的例子很棒!不过,这些代码对我来说有点复杂,难以理解其中发生了什么。您能否请修饰一下您的代码,以便像我这样的其他人可以轻松把它作为多级下拉菜单应用到自己的项目中,只需放置最上面的 ul 类? - Shams Sujon

2

document.querySelectorAll('li').forEach(element => element.addEventListener('click', event => {
  event.preventDefault();
  event.stopPropagation();

  let innerLists = element.querySelector('ul');
  if (innerLists) {
    if (!innerLists.classList.contains("show-list")) {
      document.querySelector("ul").querySelectorAll("ul").forEach(elm => {
        if (!isDescendant(elm, element)) {
          elm.classList.remove("show-list")
        }
      });
    }

    innerLists.classList.toggle("show-list");
  }
}));

function isDescendant(parent, child) {
  let node = child.parentNode;
  while (node != null) {
    if (node === parent) {
      return true;
    }
    node = node.parentNode;
  }
  return false;
}
a {
  display: inline-block;
  width: 100%;
  height: 20px !important;
  padding: 5px 10px;
  color: white;
}

nav ul,
nav ul ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

nav ul {
  width: 200px;
  background: dodgerblue;
}

nav ul ul {
  background: rgb(13, 130, 141);
  margin-left: 15px;
  border-left: 1px dashed white;
  /* height: 10px; */
  transition: max-height .5s ease-in-out;
}

nav ul ul ul {
  background: rgb(1, 8, 8);
  margin-left: 15px;
  border-left: 1px dashed white;
  /* height: 10px; */
  transition: max-height .5s ease-in-out;
}

nav ul ul ul ul {
  background: rgb(10, 41, 179);
  margin-left: 15px;
  border-left: 1px dashed white;
  /* height: 10px; */
  transition: max-height 0.5s ease-out;
}

ul li ul {
  max-height: 0 !important;
  transition: max-height 1s ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

.show-list {
  max-height: 500px !important;
  transition: max-height 1s ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}
<nav>
  <ul>
    <li><a href="javascript:void(0)">Home</a></li>
    <li><a href="javascript:void(0)">Dropdown 1 </a>
      <ul>
        <li><a href="javascript:void(0)"> Menu 1</a></li>
        <li><a href="javascript:void(0)">Menu 2</a>
          <ul>
            <li><a href="javascript:void(0)"> 1 Sub Menu 1</a></li>
            <li><a href="javascript:void(0)"> 1 Sub Menu 2</a></li>
            <li><a href="javascript:void(0)"> 1 Sub Menu 3</a></li>
          </ul>
        </li>
        <li><a href="javascript:void(0)">Menu 3</a>
          <ul>
            <li><a href="javascript:void(0)"> 1 Sub Menu 1</a></li>
            <li><a href="javascript:void(0)"> 1 Sub Menu 2</a></li>
            <!-- <li><a href="javascript:void(0)"> 1 Sub Menu 3</a> </li> -->
          </ul>
        </li>
      </ul>
    </li>
    <li><a href="javascript:void(0)">Dropdown 2 </a>
      <ul>
        <li><a href="javascript:void(0)">Menu 2 </a>
          <ul>
            <li><a href="javascript:void(0)"> 2 Sub Menu 1</a></li>
            <li><a href="javascript:void(0)"> 2 Sub Menu 2</a></li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</nav>

注意:对于简单的下拉菜单任务,您不需要编写大量JS代码。


不会使之前的 ul 列表折叠起来... 如果您在我的问题片段中单击 Dropdown 1Dropdown 2,您将看到它如何工作。 - نور
@noor 现在进行检查。 - Muhammad Daniyal
首先感谢您的回复,但是动画效果没有正常工作。如果您将我的示例“Dropdown 1”和“Dropdown 2”进行比较,我认为您可以找出问题所在。这也是我写了很多代码的原因,也许有其他替代方法。 - نور
请再次检查。 - Muhammad Daniyal
如果您使用 max-height: 500px !important;,当存在更多的 li 项时会出现错误... - نور

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接