无法复现Bootstrap列表组JS示例效果,求原因及解决方法
问题概述
尝试复现Bootstrap 5.2列表组的JavaScript交互行为,做了两处修改:用JS动态生成HTML标签、采用嵌套列表结构。目前遇到两个问题:
- 列表项的激活高亮功能失效,右侧内容无法切换
- 需要实现父项点击时嵌套列表的折叠效果
问题原因
激活与内容切换失效
- 属性拼写错误:
data-bs-toggle被写成data-bs-toogle,Bootstrap无法识别错误属性,导致标签切换逻辑不触发 - 嵌套结构错误:将子列表直接挂载到父
<a>标签下,破坏了Bootstrap列表组的预期DOM结构;递归判断时错用了nodeTgt.children.length,应该判断当前节点node.children.length - 事件冲突:手动添加的
click事件里调用e.stopPropagation(),阻止了Bootstrap内置事件的执行
- 属性拼写错误:
折叠功能缺失
- 未添加Bootstrap折叠相关的属性和DOM容器,也未实现折叠状态切换的逻辑
解决方法
一、修复激活高亮与内容切换
- 修正属性拼写:将
data-bs-toogle改为data-bs-toggle - 调整DOM结构:子列表不要直接放在父
<a>标签内,而是在父项后添加独立的折叠容器 - 修正递归判断:把
if(nodeTgt.children.length > 0)改为if(node.children.length > 0),确保只有当前节点有子项时才生成嵌套列表 - 移除事件干扰:删除
click事件中的e.stopPropagation(),或直接简化事件逻辑
二、添加父项折叠功能
- 给有子项的父列表项添加折叠触发属性:
data-bs-toggle="collapse",并指定目标折叠容器的ID - 创建折叠容器:用
<div>包裹子列表,添加collapse类,可加show默认展开,不加则默认折叠 - 添加样式标识:给有子项的父项添加右侧箭头,提升交互体验
完整修改代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Nested Bootstrap List Group</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous"> <style> /* 为有子项的父项添加箭头标识 */ .has-children::after { content: " ▼"; float: right; font-size: 0.8em; } .has-children.collapsed::after { content: " ▶"; } </style> </head> <body> <div id="tree"></div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script> <script> function clearChildren(node){ while(node.children.length > 0) node.removeChild(node.lastElementChild) } let idCounter = 0 function createNode(name,meta={}){ return { name, id: `node-${idCounter++}`, // 给ID加前缀,避免和其他元素冲突 children:new Array(), meta } } function makeTreeTable(parentElement, rootNode){ clearChildren(parentElement) parentElement.setAttribute('class', 'row') // 左侧树形列表容器 const divTree = document.createElement('div') divTree.setAttribute('class','col-8') divTree.setAttribute('id', 'div-tree') parentElement.appendChild(divTree) // 右侧内容容器 const divMeta = document.createElement('div') divMeta.setAttribute('class','col-4') divMeta.setAttribute('id', 'div-node') parentElement.appendChild(divMeta) const metaDisplay = document.createElement('div') metaDisplay.setAttribute('class','tab-content') metaDisplay.setAttribute('id','nav-tabContent') divMeta.appendChild(metaDisplay) maketree(divTree, rootNode, metaDisplay) } let activeMeta = 'tab-pane fade show active' function createMetaList(treeNode){ const metaList = document.createElement('div') metaList.setAttribute('id', `${treeNode.id}-meta`) metaList.setAttribute('class',activeMeta) activeMeta = 'tab-pane fade' metaList.setAttribute('role','tabpanel') metaList.setAttribute('aria-labelledby',treeNode.id) for (const [key, value] of Object.entries(treeNode.meta)) { const metaItem = document.createElement('div') metaItem.innerHTML = `${key} : ${value}` metaList.appendChild(metaItem) } return metaList } let activeNode = 'list-group-item list-group-item-action active' function maketree(parentElement, nodeTgt, metaDisplay) { const ul = document.createElement("div"); ul.setAttribute('class', 'list-group') ul.setAttribute('role','tablist') for(const node of nodeTgt.children){ const listItem = document.createElement("a"); listItem.innerHTML = node.name; listItem.setAttribute("id", node.id); listItem.setAttribute("class", activeNode) activeNode = 'list-group-item list-group-item-action' // 修正属性拼写 listItem.setAttribute('data-bs-toggle','list') listItem.setAttribute('role','tab') listItem.setAttribute('href',`#${node.id}-meta`) listItem.setAttribute('aria-controls',`${node.id}-meta`) ul.appendChild(listItem); metaDisplay.appendChild(createMetaList(node)) // 只有当前节点有子项时才创建嵌套列表 if(node.children.length > 0){ // 添加父项标识类 listItem.classList.add('has-children') // 创建折叠容器 const collapseDiv = document.createElement('div') const collapseId = `collapse-${node.id}` collapseDiv.setAttribute('id', collapseId) collapseDiv.setAttribute('class', 'collapse show') // 默认展开,可改为collapse默认折叠 ul.appendChild(collapseDiv) // 给父项添加折叠触发属性 listItem.setAttribute('data-bs-toggle','collapse') listItem.setAttribute('data-bs-target',`#${collapseId}`) // 递归创建子列表 maketree(collapseDiv, node, metaDisplay) } } parentElement.appendChild(ul) } function makeMeta(i){ return{ meta1:`this is ${i}`, meta2:`this is ${++i}`, meta3:`this is ${++i}`, } } // 构建树形数据 const root = createNode('root', makeMeta(0)) root.children.push(createNode('node1', makeMeta(1))) root.children.push(createNode('node2', makeMeta(2))) root.children.push(createNode('node3', makeMeta(3))) const node1 = root.children[0] const node2 = root.children[1] const node3 = root.children[2] node1.children.push(createNode('node11', makeMeta(11))) node1.children.push(createNode('node12', makeMeta(12))) node2.children.push(createNode('node21', makeMeta(21))) node2.children.push(createNode('node22', makeMeta(22))) node3.children.push(createNode('node31', makeMeta(31))) node3.children.push(createNode('node32', makeMeta(32))) const node11 = node1.children[0] const node12 = node1.children[1] const node31 = node3.children[0] const node32 = node3.children[1] node11.children.push(createNode('node111', makeMeta(111))) node11.children.push(createNode('node112', makeMeta(112))) node12.children.push(createNode('node121', makeMeta(121))) node12.children.push(createNode('node122', makeMeta(122))) node31.children.push(createNode('node311', makeMeta(111))) node31.children.push(createNode('node312', makeMeta(112))) node32.children.push(createNode('node321', makeMeta(121))) node32.children.push(createNode('node322', makeMeta(122))) makeTreeTable(document.querySelector('#tree'), root) </script> </body> </html>
内容的提问来源于stack exchange,提问作者Rodrigo Belli
相关产品推荐
相关产品推荐

