如何用原生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'); }); });
代码说明
- 层级计算:通过循环统计链接文本开头的下划线数量,确定当前项的层级,同时清理掉下划线文本。
- 层级栈管理:用栈结构跟踪每个层级的父容器,确保每个列表项插入到正确的嵌套位置。
- 动态创建子菜单:当需要嵌套子菜单时,自动创建对应的
ul容器并添加类名。 - 样式类处理:最后给所有包含子菜单的
li添加has-sub类,和原jQuery插件的效果保持一致。
内容的提问来源于stack exchange,提问作者Muzammil
相关产品推荐
相关产品推荐

