You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用原生JavaScript将单层HTML无序列表转为嵌套列表?

如何用原生JavaScript将单层HTML列表转换为多层嵌套列表?

我需要实现一个功能,把单层的HTML列表转换成多层嵌套列表,规则是通过链接文本开头的下划线数量判断层级:无下划线是顶级菜单,单个下划线是一级子菜单,两个下划线是二级子菜单。

转换前的HTML代码

<ul>
    <li><a>Main</a></li>
    <li><a>_Sub Menu</a></li>
    <li><a>__Sub Sub Menu-1</a></li>
    <li><a>__Sub Sub Menu-2</a></li>
    <li><a>__Sub Sub Menu-3</a></li>
</ul>

期望的转换后输出

<ul>
    <li>
        <a>Main</a>
        <ul>
            <li>
                <a>Sub Menu</a>
                <ul>
                    <li><a>Sub Sub Menu-1</a></li>
                    <li><a>Sub Sub Menu-2</a></li>
                    <li><a>Sub Sub Menu-3</a></li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

现有可运行的jQuery插件代码

我已经有一段能实现该功能的jQuery插件,但需要改用原生JavaScript实现:

! function(a) {
  a.fn.menuMaker = function() {
    return this.each(function() {
      var $t = a(this),
        b = $t.find('.LinkList ul > li').children('a'),
        c = b.length;
      for (var i = 0; i < c; i++) {
        var d = b.eq(i),
          h = d.text();
        if (h.charAt(0) !== '_') {
          var e = b.eq(i + 1),
            j = e.text();
          if (j.charAt(0) === '_') {
            var m = d.parent();
            m.append('<ul class="sub-menu m-sub"/>');
          }
        }
        if (h.charAt(0) === '_') {
          d.text(h.replace('_', ''));
          d.parent().appendTo(m.children('.sub-menu'));
        }
      }
      for (var i = 0; i < c; i++) {
        var f = b.eq(i),
          k = f.text();
        if (k.charAt(0) !== '_') {
          var g = b.eq(i + 1),
            l = g.text();
          if (l.charAt(0) === '_') {
            var n = f.parent();
            n.append('<ul class="sub-menu2 m-sub"/>');
          }
        }
        if (k.charAt(0) === '_') {
          f.text(k.replace('_', ''));
          f.parent().appendTo(n.children('.sub-menu2'));
        }
      }
      $t.find('.LinkList ul li ul').parent('li').addClass('has-sub');
    });
  }
}(jQuery);

我编写的有问题的原生JavaScript代码

我自己尝试写了等效的原生JS代码,但无法正常运行:

document.addEventListener("DOMContentLoaded", function () {
  var b = document.querySelectorAll("#menu li a"),
    c = b.length;
  for (var i = 0; i < c; i++) {
    var d = b[i],
      h = d.innerText;
    if (h.charAt(0) !== "_") {
      var e = b[i + 1],
        j = e.innerText;
      if (j.charAt(0) === "_") {
        var m = d.parentNode;
        var subMenu = document.createElement("ul");
        subMenu.className = "sub-menu";
        subMenu.appendChild(m);
        console.log(m.querySelector(".sub-menu"));
      }
    }
    if (h.charAt(0) === "_") {
      d.innerText = h.replace("_", "");
      var name = document.createElement("div");
      d.appendChild(name);
    }
  }
});

解决方案

下面是可以实现需求的原生JavaScript代码,核心思路是通过下划线数量判断层级,维护一个层级栈来跟踪当前各个层级的父容器,遍历每个列表项时将其插入到对应层级的父菜单中:

document.addEventListener("DOMContentLoaded", function() {
  // 获取目标ul列表(可根据实际场景调整选择器,比如用id选择)
  const parentUl = document.querySelector('ul');
  // 获取所有li元素并转为数组,方便遍历操作
  const listItems = Array.from(parentUl.querySelectorAll('li'));
  
  // 层级栈:栈顶元素是当前要插入子项的父容器,初始为顶级ul
  const levelStack = [parentUl];
  // 先清空顶级ul内容,后续重新插入处理后的项
  parentUl.innerHTML = '';

  listItems.forEach(item => {
    const link = item.querySelector('a');
    let text = link.textContent.trim();
    
    // 计算当前项的层级:统计开头的下划线数量
    let level = 0;
    while (text.startsWith('_')) {
      level++;
      text = text.slice(1);
    }
    // 更新链接文本,去掉下划线
    link.textContent = text;

    // 调整层级栈,确保栈顶是当前层级对应的父容器
    while (levelStack.length > level + 1) {
      levelStack.pop();
    }
    const currentParent = levelStack[level];

    // 如果当前层级不是顶级,且父容器没有子ul,则创建子ul容器
    if (level > 0 && !currentParent.querySelector('ul')) {
      const subUl = document.createElement('ul');
      // 根据层级添加对应类名,和原jQuery插件逻辑对齐
      subUl.className = level === 1 ? 'sub-menu m-sub' : 'sub-menu2 m-sub';
      currentParent.appendChild(subUl);
      levelStack.push(subUl);
    }

    // 将当前li插入到对应父容器中
    currentParent.appendChild(item);

    // 将当前li加入栈,作为下一层级的父容器(如果后续有子项)
    levelStack.push(item);
  });

  // 给包含子菜单的li添加has-sub类
  document.querySelectorAll('li:has(ul)').forEach(li => {
    li.classList.add('has-sub');
  });
});

代码说明

  1. 层级计算:通过循环统计链接文本开头的下划线数量,确定当前项的层级,同时清理掉下划线文本。
  2. 层级栈管理:用栈结构跟踪每个层级的父容器,确保每个列表项插入到正确的嵌套位置。
  3. 动态创建子菜单:当需要嵌套子菜单时,自动创建对应的ul容器并添加类名。
  4. 样式类处理:最后给所有包含子菜单的li添加has-sub类,和原jQuery插件的效果保持一致。

内容的提问来源于stack exchange,提问作者Muzammil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 01:45:34