
简介纯CSS族谱树形结构是一份面向Web前端开发者与初学者的可运行示例聚焦于用纯样式方案实现家谱、组织架构、文件目录、决策树等层级数据的可视化展示可平滑嵌入HTML5或PHP等现有Web项目免去引入大型图库的负担。压缩包仅2KB内含1个HTML文件轻量且可直接在浏览器中打开并修改无需复杂配置适合快速理解CSS布局与伪类在树形结构中的运用。该资源已有1270人学习下载对于需要低成本搭建层级导航或递归流程界面的场景具有不错参考价值。示例源码清晰展示了纯CSS树形布局的完整写法涵盖display、position与:before/:after绘制父-子连接线利用:hover增强交互反馈同时讨论无限级扩展的分层定位策略并延伸响应式设计、懒加载及使用Sass整理样式等二次开发思路。读者既能直接复用现有布局也可按项目需求自定义节点形状、配色与展开折叠行为适用于学习DOM与CSS协作逻辑的前端入门者也能为有经验的开发者提供快速改造的模板。1. 纯CSS族谱树形结构不写一行脚本把世系图排版做到页面上前阵子做一个家族文化展示页需求听起来很朴素首页放一张历代世系图不引入图标库最好连 JavaScript 都不写。一开始我觉得这是在给自己挖坑族谱树既要层级连线又要整支高亮纯 CSS 能撑住吗真正把版式调通后发现利用嵌套列表和伪元素纯 CSS 的族谱树不但画得出来而且比图表插件的可控性强很多。这份资源就是那套方案的完整沉淀零依赖、纯 CSS 的族谱树形结构适合放在家族文化站点、个人站也可以直接拿来做组织架构原型。下载后是一组干净的 HTML/CSS 文件没有 node_modules没有打包配置改数据改样式都在这几个文件里完成很适合前端入门者拆开研究也适合老手拿去快速复用。2. 结构与核心原理用嵌套列表承载家族关系用伪元素补上所有连线2.1 目录结构与文件分工整个项目没有引入构建工具文件结构非常直观family-tree/ ├── index.html # 演示页面完整的三代世系示例 ├── css/ │ └── family-tree.css # 核心样式布局、连线、高亮全部集中于此 └── assets/ └── 示例数据说明.md # 数据与页面结构之间的对应关系说明index.html 里只放语义化的 HTML 标签不出现任何行内样式family-tree.css 承担全部视觉工作包括树形结构、伪元素连线、悬停高亮示例数据说明则记录了“一个 li 代表一代人”的映射约定方便往自己的数据上迁移。三个文件的职责切割得很干净结构、表现、数据说明互不纠缠。2.2 从嵌套列表到层级关系一棵树本身就是 ul/li族谱树的数据模型不需要额外的抽象HTML 的嵌套列表天然就是一棵树。祖先在上层后代通过不断嵌套的 ul 向下延伸页面结构本身就是数据结构。ul classfamily-tree li classperson span classnode曾祖父/span ul li classperson span classnode祖父/span ul li classpersonspan classnode父亲/span/li li classpersonspan classnode叔父/span/li /ul /li /ul /li /ul这段标记定义了四层结构最外层 ul 是树的根每个 li.person 对应一位家族成员span.node 是显示名字的节点块一个成员的子代只要再包一层 ul 放进去即可。要加一代人就复制一个 li 并给它补上内部 ul要删一个分支直接移除对应 li 块。数据与结构完全等价后期无论人工维护还是脚本生成都很顺手。2.3 连线的本质用伪元素画线而不是图片或 SVG很多树形组件会把连线做成背景图或内嵌 SVG。这个项目没有走那条路因为背景图一旦层级缩进调整就要重新切图SVG 又需要逐个节点计算坐标。这里的核心思路是让每个 li 自己通过 ::before 和 ::after 画出属于自己的那一段线。.family-tree ul { list-style: none; margin: 0; padding-left: 45px; } .family-tree li { position: relative; padding: 18px 0 0 24px; }ul 向右缩进 45px是为了给前面的连线留出画布空间li 设置 position: relative是为了让伪元素以 li 自身为参照进行绝对定位。每一条线都不是预先画好的完整图画而是由大量小线段拼接而成这个 li 负责自己与父节点之间的连接同时用 height 100% 向下延伸覆盖到最后一个后代最终在视觉上连成一条完整的树干。这种做法的好处是改一个节点的颜色只会影响它管辖的那一小段不会像整张背景图那样牵一发动全身。3. 核心模块拆解竖线、横线与整支高亮是怎么画出来的3.1 根节点不带上线子节点带 T 形线先看完整的基础连线样式这是整个项目的骨架.family-tree, .family-tree ul { list-style: none; margin: 0; padding: 0; } .family-tree { padding-left: 12px; } .family-tree li { position: relative; padding: 22px 0 0 28px; } .family-tree li::before, .family-tree li::after { content: ; position: absolute; top: 0; left: 0; width: 0; height: 100%; border-left: 2px solid #b7b7b7; } .family-tree li::after { top: 22px; width: 28px; height: 22px; border-top: 2px solid #b7b7b7; border-left: none; } .family-tree li::before { display: none; } .family-tree li::after { border-top: none; } .family-tree li:last-child::before { height: 22px; }逐个看四个关键规则。li::before 是从当前 li 顶部一直延伸到内容区底部的竖线高度 100% 意味着它会把子节点的区域也涵盖进去树的主干就是靠它拉出来的。li::after 是拐向子节点的横线top 22px 正好对齐 li 顶部的 paddingwidth 28px 对应左侧 padding于是横线从左侧边缘伸到内容区起点形成 T 形连接。根节点用 .family-tree li::before 去掉顶部竖线否则页面开头会出现一条悬空的天线最后一个子节点用 height: 22px 截断竖线避免整条线穿出末端。这里有几个参数需要联动修改28px 是节点缩进改动它时 ::after 的 width、left 要一起调22px 是节点自身行高产生的空隙它和 li 的 padding-top 必须一致否则横线会上下错位2px 是线宽如果换成 1px 视觉更细但打印出来容易看不清我一般保持 2px。3.2 横向家谱父子从左到右延伸的另一种版面竖排树适合表现“世代向下传承”但有些族谱更强调“一支一房往右摊开”需要横向布局。项目在 CSS 文件里预留了一套横向变体通过加一个修饰类来切换.family-tree--horizontal ul { display: flex; flex-direction: column; } .family-tree--horizontal .person { display: flex; align-items: flex-start; position: relative; } .family-tree--horizontal .person ul { margin-left: 24px; padding-left: 24px; border-left: 2px solid #b7b7b7; } .family-tree--horizontal .person .node { margin-right: 24px; white-space: nowrap; }横向布局的思路是把“向下延伸”改成“向右延伸”每个 .person 内部用 flex 让节点块和子列表排在一行父节点在左子节点在右子列表左侧画一条竖线作为主干节点之间的兄弟关系靠这条左边界线体现。什么时候用横版我一般看两个条件世系分支少但世代深横版能更好地利用宽屏或者页面本身要嵌进横向滚动的展示容器里竖版嵌套太多会把高度撑爆。横版对深度没有竖版那么敏感因为缩进方向是横向的屏幕宽了就多铺一点。3.3 悬停高亮一整支联动子级与祖先的完整链路用户看族谱时最常用的操作是鼠标移到某个人身上顺着线看清他整个支系。纯 CSS 做这件事比想象中要绕因为高亮不能只作用于当前节点还要把与它相连的上下级线段全部变色.family-tree li:hover .node { background: #2f6f4f; color: #fff; } .family-tree li:hover ul li::before, .family-tree li:hover ul li::after { border-color: #2f6f4f; } .family-tree li:hover::before, .family-tree li:hover::after { border-color: #2f6f4f; }第一条规则点亮当前成员的名字块第二条规则把当前 li 直接子级区域内所有线段变绿这覆盖了从当前节点往下延伸的整段竖线和每个子节点入口的横线第三条规则负责当前节点自己头上的线。三条配合起来鼠标悬停时从祖先线、当前节点到全部后代线都变成同一种颜色视觉上就像用荧光笔描了一遍。注意这里有个容易被忽略的 z-index 问题伪元素和 .node 处于同一个层叠上下文而连线是绝对定位可能会盖在文字块上方影响点击。我会给 .node 补一条 position: relative; z-index: 1确保节点永远悬浮在线条之上hover 行为才稳定。4. 避坑笔记伪元素画线的经典翻车现场与排查顺序4.1 节点上方的线全部消失页面变成一列纯文本现象把 HTML 写好后打开浏览器发现树形结构完全没线一眼看去就是普通的嵌套列表。原因绝大多数情况下是伪元素的 content 忘了写。::before 和 ::after 必须显式声明 content: 才会渲染哪怕内容是空的另外 li 没设 position: relative也会让伪元素相对更外层容器定位线跑到页面边缘以外视觉上等同于消失。解决先检查 CSS 里是否每个 ::before/::after 都带 content: 再看 li 是否设了 position: relative。这两个是画线的基础前提缺任何一个都白搭。4.2 根节点头顶冒出一截天线现象页面最上方、第一个节点的正上方出现一段竖线看起来像树顶插了根避雷针。原因根节点也是 li默认的 li::before 规则把它的头顶竖线也画出来了。很多第一次写的人只加了通用规则忘了根节点是特殊位置。解决用 .family-tree li::before 选择器单独屏蔽根节点的上行竖线同时用 .family-tree li::after 去掉根节点的横线。 子选择器在这里是关键它只命中第一层 li不会误伤内部子节点。4.3 最后一个节点下方多出一截竖线怎么也截不断现象每个分支的末位成员下方竖线一直延伸到父容器底部甚至穿出整棵树。原因li::before 的 height 设成了 100%它会延伸到 li 对应的整个区域底部。对于最后一个 li没有后续兄弟节点来“接住”这条线于是它就裸露在外面。解决li:last-child::before 单独把高度改成 22px也就是只保留从 li 顶部到横线位置的这段竖线在横线处自然收口。这个数值要和 li 的 padding-top 保持一致否则收口位置会偏。4.4 悬停高亮时当前节点和子级亮了父级到它的线却还是灰色现象鼠标移到节点上名字块和下方支线变色了但节点上方、从父节点连过来的那一段竖线纹丝不动。原因当前节点上方的线其实归父级 li 的 ::before 管它跨越了包括当前节点在内的整段子树区域。只给当前 li 的 ::before::after 变色永远碰不到父级那条线。解决把父级选择器一并写进 hover 规则.family-tree li:hover::before 负责当前节点自己头上的线.family-tree li:hover ul li::before 负责子区域的线再配合 .family-tree li:hover .node 点亮节点三层联动才完整。我一般把颜色抽成 CSS 变量修改主题色时只动一处。4.5 层级一深左边缩进把页面撑出横向滚动条现象族谱加到五六代以后每层缩进 28px整棵树越来越宽手机上直接出现横向滚动条。原因树形结构的宽度是逐层累加的嵌套越深必然越宽这是结构决定的不是样式写错。解决给最外层 .family-tree 包一个 overflow-x: auto 的容器让深层树在容器内部横向滚动而不是撑崩整个页面布局或者在第 4 层之后改用紧凑缩进模式把 padding-left 从 28px 降到 18px视觉上会明显收敛。5. 运行与验证把代码跑起来并用开发者工具检查每一根线的盒子5.1 本地运行不需要构建工具一条命令起服务这个项目没有任何依赖理论上双击 index.html 就能在浏览器里看到效果。不过双击打开用的是 file:// 协议某些浏览器对本地文件有一些安全限制且后续如果要加 fetch 加载数据就会直接失败。我建议还是起一个本地静态服务这也是交付给别人的标准做法cd family-tree python3 -m http.server 8080打开浏览器访问 http://localhost:8080 就能看到渲染结果。python3 是系统自带的能力如果你机器上没有 python3也可以用 npx serve . 替代效果一样。起服务的主要好处是后续引入数据文件时路径和接口行为与线上环境一致改完直接刷新页面即可验证不用来回切换协议。5.2 用开发者工具检查伪元素线到底画在哪纯 CSS 画线最大的障碍是“看不到线的盒子”出了问题很难定位。其实浏览器开发者工具天然支持查看伪元素根本不用猜操作路径是这样打开页面按 F12在 Elements 面板里选中任意一个 li 标签Styles 区域下方会单独列出 ::before 和 ::after 两个伪元素的样式块用鼠标点击伪元素条目页面上的对应区域会被高亮框起来线所在的矩形盒子、线宽、位置全部可视化呈现。如果要看盒子具体覆盖范围切到 Layout 面板勾选 Show element box就能看到 padding 区域和线的相对位置。我最常用的是临时修改法在 Styles 面板里直接给 ::after 改一个醒目的背景色比如 background: rgba(255, 0, 0, 0.3)此时横线区域会变成红色半透明块一眼就能看出它是否和节点对齐。排查完刷新页面就恢复正常完全不影响源码。这个技巧对 4.3 节的“竖线多一截”问题特别有效能直观看到 last-child 那条线的实际高度。5.3 替换示例数据把示例家族改成你自己的世系跑通之后的下一个动作通常是替换成自己的家族数据。竖排树的数据维护极其简单就是改 HTML 缩进结构。比如在「祖父」的 ul 里加一个「姑母」的分支li classpersonspan classnode祖父/span ul li classpersonspan classnode父亲/span/li li classpersonspan classnode姑母/span/li /ul /li保存刷新后新节点会自动出现在祖父下方与父亲并列连线也自动补好。删除分支则反向操作删掉整个 li 块。这里有一个容易忽略的约定一个 li.person 内部最多只放一个直接子级 ul如果放了多个 ulCSS 的 hover 联动和 last-child 截断逻辑都会产生错乱因为多棵子树共享同一个父节点的连线区域。需要并排展示两棵分支时正确做法是在父节点的 ul 里加两个平级 li而不是在一个 li 里塞两个 ul。6. 改造思路从静态模板升级成数据驱动的族谱生成器6.1 用递归把平铺数据渲染成嵌套列表静态 HTML 适合数据固定的小型展示一旦家族分支超过几十个节点手工维护缩进就变得很痛苦。常见做法是写一个递归渲染函数把结构化数据一次性转成嵌套列表结构。const members [ { id: 1, name: 曾祖父, children: [2] }, { id: 2, name: 祖父, children: [3, 4] }, { id: 3, name: 父亲, children: [] }, { id: 4, name: 叔父, children: [] } ]; function renderTree(ids, dataset) { return ul ids.map(id { const node dataset.find(item item.id id); const childHtml node.children.length ? renderTree(node.children, dataset) : ; return li classpersonspan classnode${node.name}/span${childHtml}/li; }).join() /ul; } document.getElementById(tree-root).innerHTML renderTree([1], members);函数签名里的 ids 是当前要渲染的节点 id 数组dataset 是包含全部成员的数据源。它先为每个 id 找到对应成员再递归渲染每个成员的 children直到没有子节点才停止整个树的深度完全由数据决定。生产环境数据量较大的时候建议先用一个 Map 把 id 到成员的映射建好替代这里的 find因为 find 每次都要遍历全量数组。6.2 无JS折叠用 details 与 summary 收纳分支家族支系多的时候页面会很冗长。纯 CSS 方案里藏着一个很好的交互替代品原生 details 元素。把一个分支包进 details 后折叠展开由浏览器原生管理仍然不用写一行脚本。li classperson details summaryspan classnode祖父/span/summary ul li classpersonspan classnode父亲/span/li li classpersonspan classnode叔父/span/li /ul /details /li注意折叠后 li::before 的 height 100% 仍然按整棵子树算展开收起时线的位置会有变化所以折叠分支最好单独配一个简化连线样式比如收起状态下隐藏子区域内的线段。这个细节是纯 CSS 方案里少有的“需要额外适配”的地方。6.3 导出与打印走向可交付的文档族谱最终常常要打印出来送给长辈。打印样式和屏幕样式是两套逻辑屏幕上的绿色高亮、灰线放到纸上效果很差我一般补一段 print 规则把连线和文字全部转成黑色media print { .family-tree li::before, .family-tree li::after { border-color: #333 !important; } .family-tree .node { background: #fff !important; color: #000 !important; border: 1px solid #333; } }深色块在墨水打印机上既费墨又看不清文字转成白底黑字是最稳妥的交付形态。经过这轮改造这套纯 CSS 族谱树就从“一个演示页面”变成了“可维护、可交互、可打印”的完整小系统。从那以后我每次遇到“能不能不引入库就画一棵树”这类需求都会先确认三个问题数据是静态还是动态、打印要不要支持、是否需要分支折叠。确认完再动手纯 CSS 方案基本能覆盖八成场景而且交付物简单到可以直接丢给后端同事去维护。希望帮到你。本文还有配套的精品资源点击获取