资讯动态

纯CSS族谱树形结构:零JS实现五代人层级排布与连线

发布时间:2026/10/14 16:35:25 来源:尧图企业网站定制
简介这是一份面向前端初学者与Web开发者的纯CSS族谱树形结构示例资源聚焦于用样式表实现层次化数据可视化可应用于家谱展示、企业组织结构图、文件目录树等场景。压缩包内共1个文件为单个html页面体积约2KB轻量易读打开即可查看完整结构与样式写法。资源通过display、position、float等布局属性配合:before、:after伪元素绘制节点连接线并借助:hover增强交互反馈展示了无限级扩展的递归思路与节点间距、连接线长度的处理方式。读者可从中掌握纯CSS构建树形结构的核心技巧并在此基础上进行二次开发如自定义节点形状与配色、增加展开折叠交互、使用媒体查询实现响应式适配以及通过懒加载与选择器精简优化大型族谱的渲染性能。目前已有1270人学习浏览适合希望摆脱复杂插件、用原生CSS完成层级可视化的开发者参考借鉴。1. 纯CSS族谱树形结构不写一行 JS怎么把五代人排明白很多人第一次做族谱、组织架构、家谱展示这类页面第一反应是找现成的树形组件或者干脆上 D3、ECharts 的树图。但真到落地时会发现数据量不大、层级固定、只是要一张能看能打印的静态图引一个几百 KB 的图表库纯属杀鸡用牛刀。纯 CSS 族谱树形结构解决的正是这个场景——用ul/li嵌套加伪元素画连线零 JavaScript 就能把父子、兄弟、配偶关系排得清清楚楚。它适合谁适合做家谱展示页、公司组织架构静态页、课程知识树、家族纪念册电子版的开发者。核心诉求通常就三条层级不能乱、连线不能断、人多了不能挤成一坨。这三条恰好也是纯 CSS 方案最容易翻车的地方。下面按「先立住原理、再动手复现、最后讲坑」的顺序拆开讲中间给可直接抄的代码和参数。2. 用 ul/li 嵌套搭骨架为什么不用 div 硬堆2.1 语义嵌套天然对应辈分层级族谱的本质是一棵树一个人有若干子女子女再各有子女。HTML 里最贴合这个结构的标签就是嵌套列表——外层ul是某一代li是一个个体li里再嵌一个ul就是他的下一代。这种写法的好处是即使 CSS 全部失效页面依然是一份可读的缩进大纲这对无障碍访问和打印都友好。用div硬堆也能做但你得自己维护层级 class改一代人要动一片结构。嵌套列表的层级是结构自带的CSS 里用后代选择器就能按深度分别控制这是纯 CSS 方案能成立的前提。!-- 族谱骨架ul 代表一代人li 代表一个个体 -- ul classfamily-tree li span classperson曾祖父/span !-- 嵌套 ul 即下一代 -- ul li span classperson祖父/span ul lispan classperson父亲/span/li lispan classperson叔父/span/li /ul /li lispan classperson姑祖母/span/li /ul /li /ul这段结构里.person是真正显示姓名的节点ul只负责层级容器。把「显示内容」和「结构容器」分开后面画连线时才好定位——连线画在li上内容放在span里互不干扰。2.2 横向排布flex 让同辈人自动并排默认的ul是纵向堆叠的同辈人会竖着排这不符合族谱「同辈横向、晚辈向下」的直觉。解决办法是给每一层的ul加display: flex让同辈的li横向铺开同时用justify-content: center让子节点相对父节点居中——这一步是后面连线能对称的关键。/* 每一层列表横向排列子节点整体居中于父节点下方 */ .family-tree ul { display: flex; justify-content: center; padding-top: 24px; /* 给父子之间的连线留出垂直空间 */ position: relative; /* 作为伪元素连线的定位参照 */ } .family-tree li { list-style: none; text-align: center; position: relative; /* 每个节点都是连线的定位基准 */ padding: 24px 12px 0; /* 上留连线空间左右留兄弟间距 */ }padding-top: 24px这个值不是随便定的它决定了父子之间那段竖线的长度。太小连线挤在一起太大整棵树被拉得很空。我一般从 24px 起步节点文字行高变化时再微调。padding左右各 12px 控制兄弟节点之间的最小间隙人多时靠它撑开。2.3 用伪元素画连线横线、竖线各管一段纯 CSS 画连线的核心思路是竖线用::before横线用::after分别负责「父到子」和「兄弟之间」两段。这是整个方案里最需要理解的一步理解了它后面所有调整都是改数值。/* 竖线从父节点底部往下连到横线 */ .family-tree li::before, .family-tree li::after { content: ; position: absolute; top: 0; right: 50%; border-top: 2px solid #999; /* 横线本体 */ width: 50%; height: 24px; /* 与 padding-top 一致保证竖线接得上 */ } /* 横线从节点中心往右延伸连接右侧兄弟 */ .family-tree li::after { right: auto; left: 50%; border-left: 2px solid #999; /* 竖线本体 */ } /* 只有一个孩子时不需要横线避免出现多余的一小段 */ .family-tree li:only-child::after, .family-tree li:only-child::before { display: none; } /* 第一个孩子去掉左半横线最后一个孩子去掉右半横线 */ .family-tree li:first-child::before, .family-tree li:last-child::after { border: 0 none; } /* 最后一个孩子补上圆角让连线收口更自然 */ .family-tree li:last-child::before { border-right: 2px solid #999; border-radius: 0 6px 0 0; } .family-tree li:first-child::after { border-radius: 6px 0 0 0; }逻辑说明每个li的::before画的是「这个节点左半边到中心」的横线::after画的是「中心到右半边」的横线同时::after还带一条向下的竖线。第一个孩子不需要左边的横线左边没兄弟最后一个孩子不需要右边的横线所以用:first-child和:last-child把多余的边去掉。:only-child是独生子女的特例——既没兄弟也没姐妹横线全去掉只留一条竖线。参数说明border-top和border-left的粗细这里 2px决定连线视觉重量族谱这种正式场景建议 1.52px太细打印会断。border-radius的 6px 是收口圆角纯装饰可去掉。height: 24px必须和li的padding-top相等否则竖线和横线之间会出现缝隙——这是新手最常见的翻车点。3. 把连线接对三个必调参数和一套自检方法3.1 竖线高度、横线宽度、节点间距的联动关系纯 CSS 族谱树最容易出问题的地方是连线「差一点点接不上」。根源在于三个数值互相耦合li的padding-top、伪元素的height、以及ul的padding-top。它们必须满足一个关系伪元素 height li 的 padding-top这样竖线底端正好落在横线所在的高度。/* 三个数值必须联动改一个要同步改另一个 */ .family-tree ul { padding-top: 24px; /* 父节点底部到子节点横线的距离 */ } .family-tree li { padding-top: 24px; /* 必须等于伪元素 height */ } .family-tree li::before, .family-tree li::after { height: 24px; /* 必须等于 li 的 padding-top */ }我一般把这三个值抽成一个 CSS 变量改的时候一处生效避免漏改:root { --line-gap: 24px; /* 连线垂直间距统一控制 */ } .family-tree ul { padding-top: var(--line-gap); } .family-tree li { padding-top: var(--line-gap); } .family-tree li::before, .family-tree li::after { height: var(--line-gap); }3.2 节点内容撑破布局时怎么排查当姓名很长、或者一个节点里放了头像加姓名加生卒年li会被撑宽导致兄弟节点挤在一起甚至重叠。排查顺序是先看li有没有设min-width再看.person有没有限制宽度。.family-tree li { min-width: 80px; /* 给每个节点一个最小宽度防止窄名字挤在一起 */ } .family-tree .person { display: inline-block; max-width: 120px; /* 超长名字换行而不是撑破 */ white-space: normal; word-break: break-all; padding: 6px 10px; border: 1px solid #ccc; border-radius: 4px; background: #fff; }min-width保证节点不会因为名字短而缩成一条缝max-width保证名字长时换行而不是横向撑爆。这两个值要一起调min-width太大人多了整棵树横向溢出太小短名字节点看起来像没对齐。经验值是min-width取常见名字宽度的 1.2 倍左右。3.3 用浏览器开发者工具逐层验证连线连线对不对肉眼看不准得靠开发者工具。做法是选中某个li在 Elements 面板里看它的::before和::after伪元素勾选/取消border属性观察哪一段线消失。如果取消某条边后本该断开的线还在说明那条线是别的节点画的定位错了。另一个技巧是临时给伪元素加背景色/* 临时调试用确认伪元素覆盖范围上线前删掉 */ .family-tree li::before { background: rgba(255,0,0,.2); } .family-tree li::after { background: rgba(0,0,255,.2); }红色块是左半横线蓝色块是右半横线加竖线。哪个块位置不对一眼就能看出来。这个土办法比反复改数值猜要快得多属于血泪经验。4. 避坑指南纯 CSS 族谱树最常见的 5 个翻车现场4.1 连线在 Safari 里错位半像素现象Chrome 里连线严丝合缝Safari 里横线和竖线之间有一条发丝般的缝。原因Safari 对亚像素渲染的处理和 Chrome 不同border落在半像素位置时会被舍入。解决把border宽度设成偶数2px 而不是 1.5px并给伪元素加transform: translateZ(0)触发整数像素对齐。4.2 节点多了整棵树横向溢出屏幕现象同辈有七八个节点时整棵树超出容器宽度出现横向滚动条。原因flex默认不换行justify-content: center也不会压缩子项。解决给最外层容器加overflow-x: auto并给ul设flex-wrap: nowrap明确不换行——族谱同辈换行会破坏辈分视觉宁可横向滚动。如果一定要适配窄屏用媒体查询缩小--line-gap和节点padding。4.3 独生子女节点的多余横线现象只有一个孩子的节点下方横线左右各伸出一小截像个小十字。原因:only-child的横线没去掉。解决加上li:only-child::before, li:only-child::after { display: none; }只保留竖线。这条规则要写在:first-child/:last-child规则之后否则会被覆盖。4.4 打印时连线消失或变灰现象屏幕上好好的族谱打印预览里连线几乎看不见。原因浏览器打印默认不打印背景色而细border在低分辨率打印下会变淡。解决连线用border而不是background画本方案已经是并把线宽提到 2px 以上同时加media print强制-webkit-print-color-adjust: exact。4.5 深层嵌套后选择器权重打架现象到第四五代时某些节点的连线样式突然不对。原因后代选择器.family-tree li会命中所有层级但:first-child这类伪类在不同深度下语义不同容易和别的规则冲突。解决尽量用.family-tree ul li这种直接子代选择器限定层级或者给每一代加一个>.family-tree { --gap: 24px; --line: 2px; --font: 14px; } .family-tree.compact { --gap: 12px; --line: 1px; --font: 12px; } .family-tree li { padding-top: var(--gap); font-size: var(--font); } .family-tree li::before, .family-tree li::after { height: var(--gap); border-width: var(--line); }这样「紧凑视图」和「标准视图」之间切换只需要在容器上增删compact类所有相关数值自动联动不用逐个改。参数上--gap建议不小于 12px再小连线会糊成一团--line打印场景不要低于 1.5px。再看:has()做后代高亮。假设每个节点有>/* 当某个 li 被标记为 active 时它内部所有后代节点变色 */ .family-tree li.active .person { background: #eef5ff; border-color: #4a90d9; } /* 用 :has() 反向包含 active 后代的祖先节点加浅色底 */ .family-tree li:has(.active) .person { border-color: #4a90d9; }:has()目前主流浏览器都已支持但要注意它不能用于伪元素内部只能作用在真实元素上。如果项目要兼容较老的浏览器这部分高亮还是得回退到 JS 加 class纯 CSS 只负责静态展示。最后一个实用技巧是打印适配。族谱经常要打印成纸质版加一段打印样式能让输出干净很多media print { .family-tree { --gap: 20px; --line: 2px; } .family-tree .person { border: 1px solid #000; /* 打印用纯黑边框避免灰色太淡 */ background: #fff; } .no-print { display: none; } /* 隐藏页面上的操作按钮等 */ }打印时把连线颜色统一成纯黑、线宽提到 2px是因为家用打印机对灰色和细线的还原很差屏幕上好看的#999打出来常常是一片浅灰。这个细节不注意交付的纸质族谱会显得很脏。我自己做这类页面踩得最深的一次是早期用div硬堆层级结果客户要加一代人我改了三十多个 class 名改到怀疑人生。后来换成嵌套ul加伪元素连线同样的需求只动了结构里的一层嵌套CSS 一行没改。纯 CSS 方案的价值不在于炫技而在于结构即数据、样式即规则改起来心里有底。如果你也在做族谱或组织架构这类固定层级的展示建议先把ul/li骨架和三个联动参数吃透再往上叠变量和:has()会顺很多。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑