☰
纯CSS族谱树形结构:零依赖实现家谱与组织架构图
2026/10/9 3:26:10 网站建设 项目流程

简介:一套基于纯CSS打造的族谱树形结构HTML示例,面向HTML5、PHP及Web前端开发者,用于快速实现家谱、组织架构、目录树等多级节点展示。整个资源包仅含一个HTML文件,压缩后大小2KB,无需服务器和第三方库,浏览器直接打开即可看到完整的树形布局与连接线效果。其中运用display、position、float及伪类选择器实现节点层级与父子关系,重点展示了:before/:after绘制连线和箭头、:hover增强交互的技巧,并通过递归HTML结构支持无限级扩展。同时代码注释简洁,便于二次定制节点样式、配色和响应式布局,也可在此基础上加入JavaScript实现折叠展开,并可直接嵌入现有页面。已有1270人学习浏览,适合前端入门者对照练习,也能作为实际项目中层级可视化的轻量参考模板,尤其对理解CSS定位和伪类应用很有帮助,方便日常开发使用。

1. 纯CSS族谱树形结构:别再为一张家谱图引图表库了

做族谱、家谱或是组织架构图的展示页面,很多人第一反应就是上ECharts、上D3,或者干脆后端渲染一张大图扔给前端。但我在一个实际项目里踩过完全不同的路子——需求方要求页面零依赖、可复制、能直接嵌进静态站点,并且要照顾老旧浏览器的访问者。折腾一圈下来发现,一棵像模像样的家谱树,其实用纯CSS加上嵌套合理的HTML就可以实现,连线、分支、对仗关系全部可控。这份资源就是我当时沉淀下来的模板:一套HTML骨架加一套核心CSS,能撑起五层以内的常规族谱结构,也能应对过继、多配、跨支系这类复杂场景。适合不想引库、又需要快速输出静态族谱页面的前端开发、文化数字化项目从业者和个人站长。

2. HTML骨架与数据约定:先让层级关系在文档流里成立

2.1 HTML骨架怎么搭:ul/li嵌套就是最原始的树

树形结构的第一原则,是让HTML本身先长成一棵树,而不是靠CSS去“画”出树。族谱在数据上是典型的多叉树:一个父节点下挂着几个兄弟分支,每个分支下面一层,就是下一代。语义化标签里最贴合这个模型的,是ul和li的嵌套。

<ul class="tree"> <li> <div class="node node-ancestor">始祖</div> <ul> <li> <div class="node">长房</div> <ul> <li> <div class="node">长子</div> </li> <li> <div class="node">次子</div> </li> </ul> </li> <li> <div class="node">次房</div> <ul> <li> <div class="node">长子</div> </li> </ul> </li> </ul> </li> </ul>

这段结构里,每个li代表一个家庭成员节点,节点里的文字放在div.node中方便后续加背景、连线和事件。外层ul.tree是整个树的容器,后代分支永远是li内部的又一个ul,层层嵌套,从根到叶。

逻辑说明:这种做法的核心优势在于,CSS可以通过父子选择器拿到确切的层级关系,例如ul ul表示所有更深层级的子列表,li > ul直接命中“某个节点下的直接后代”。不需要JavaScript去计算层级,也不需要给每个节点人工编号。参数上需要注意的只有一点:ul和li的默认样式必须重置,否则浏览器会渲染出项目符号和缩进,破坏连线布局。我一般会在reset区把list-style:none和margin:0铺底。

选择这个结构还有一个现实理由:旧的表格布局或者全div嵌套,要么让语义一团糟,要么让屏幕阅读器抓不到逻辑。而ul/li从HTML2.0时代就是列表语义,IE8的解析器也认识它,兼容性上天然省心。

2.2 字段与类名约定:把家谱信息映射成可渲染的DOM

HTML结构只是外壳,真正决定族谱信息能不能完整落到页面上的,是字段约定。我在这份资源里采用了一套极简的约定,不依赖后端字段,纯靠CSS类名和><li>ul.tree, ul.tree ul { display: flex; flex-direction: row; list-style: none; margin: 0; padding: 0; } ul.tree li { display: flex; flex-direction: column; align-items: center; position: relative; padding: 20px 5px 0 5px; }

逻辑说明:外层ul横向排开各房,内层li纵向堆叠“节点本身+子分支”,每个li内部又是一个横向排列的下一层ul,这就实现了“横向分支、纵向换辈”的核心布局。align-items:center保证当前节点和它下面的子分支中线对齐。参数上padding:20px 5px 0 5px的20px是垂直间距,决定连线的高度;5px是左右间距,决定兄弟节点之间的距离。间距太小线条会挤在一起,太大树又摊得太宽,实际调的时候我从20px起步,按内容宽度做增减。

3.2 连接线怎么画:伪元素加边框模拟树杈

树形结构视觉上最难的部分,是父子节点之间那些垂直线、水平线和L形转折线。不用任何图片、不用canvas,纯CSS画线的办法只有一种:用::before和::after两个伪元素,配合border和宽高去“拼”出线段。

ul.tree li::before, ul.tree li::after { content: ""; position: absolute; top: 0; right: 50%; width: 50%; height: 20px; border-top: 2px solid #888; } ul.tree li::after { right: auto; left: 50%; border-left: 2px solid #888; } ul.tree li:first-child::before, ul.tree li:last-child::after { border: 0 none; } ul.tree li:last-child::before { border-right: 2px solid #888; border-radius: 0 5px 0 0; } ul.tree li:first-child::after { border-radius: 5px 0 0 0; }

逻辑说明:这段是画线的核心。每个li都拥有一个属于自己的水平上边线,::before画左边一半,::after画右边一半,中间的转折由border-top和border-left拼接而成。第一个li只画右半段,最后一个li只画左半段,中间节点左右两段都画,这样兄弟节点之间就能连出一条完整的“T”字或“H”字衔接线。

参数说明:2px是线宽,#888是线条颜色,20px的高度必须和li的padding-top一致,这是最容易对不上的地方,后面避坑章会细讲。如果需要更粗的视觉风格,把border-top和border-left同步加粗,左右两半才能衔接平整。

还有一个必须处理的问题——根节点不应该再往上画线。根li是树的最顶层,它上面不该有竖直向上的线。我给ul.tree > li单独做了覆盖:

ul.tree > li::before, ul.tree > li::after { border: 0 none; }

这一段不写的话,根节点头顶会冒出一截悬空短线,非常突兀。

3.3 完整核心CSS:从根节点到五层分支的可控参数

资源包里的核心CSS不只是画线,还包含了节点卡片、间距节奏和状态样式。这里给出可直接用的最小完整版:

.tree-node { border: 1px solid #b5a07e; background: #faf6ef; padding: 8px 18px; border-radius: 6px; font-size: 14px; line-height: 1.4; white-space: nowrap; position: relative; z-index: 1; } .node-spouse { border-color: #c9b8a0; background: #fff; } ul.tree li { display: flex; flex-direction: column; align-items: center; position: relative; padding-top: 30px; } ul.tree li .tree-node { z-index: 2; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08); }

逻辑说明:z-index在这里很关键。伪元素画出的线要穿过节点之间的空隙,如果节点卡片不设z-index并加背景,线条会直接压在文字上。给节点设z-index:2之后,卡片会盖住线条,视觉上形成“线从节点边缘伸出”的干净效果。参数上padding:8px 18px决定了卡片的最小尺寸,white-space:nowrap防止人名和配偶名被拆成两行。

这里对五层分支做一个明确的建议:当嵌套层级超过五层时,横向空间会急剧膨胀,一个二层十兄弟的节点树,最底层可能产生超过2000px的宽度。我的习惯是超过五层就启用折叠方案(后面第6章会写),而不是继续加宽页面。如果非要展示全谱,考虑把页面做成横向滚动,ul.tree外层套一个overflow-x:auto的容器。

4. 复杂场景适配:多配偶、过继与跨支系连线的处理

4.1 多配偶与继配:一个父节点配多个水平节点的处理

真实族谱里,多配、继配是常见情况——一个男性节点后面跟着两个或三个女性节点,平级横排。很多人会在HTML里把它们写成同一个li里的多个节点,这是错误示范,因为后续子代分支无法区分哪一条线是谁生的。

<li> <div class="tree-node"> 某公 </div> <div class="tree-node node-spouse"> 元配某氏 </div> <div class="tree-node node-spouse"> 继配某氏 </div> </li>

逻辑说明:我一般会把配偶节点放进同一个li,让它们跟随父节点一起居中。但这里有个关键边界——多配场景下,如果不同配偶的子女要区分子树,就必须在HTML上拆成独立分支,用><li> <div class="tree-node">某公</div> <ul> <li>.tree-node[data-adopted="true"] { border-style: dashed; background: #f0ede6; } .tree-node[data-adopted="true"]::after { content: "继"; position: absolute; right: -10px; top: -8px; font-size: 10px; color: #a08060; }

逻辑说明:加一个小角标,让读者一眼看出节点是过继关系,不需要在文字里反复写“过继”两个字。参数上right:-10px和top:-8px是把角标推出节点卡片的右上角,注意不要让它超出li的边界,否则会被overflow截断。

4.3 女系与赘婿:上下翻转让分支方向变更

现代族谱除了父系单传,还有女系后裔和赘婿入谱的情况。女系节点的处理,我通常在>li[data-branch="matrilineal"] > ul { border-left: 2px dashed #c0a882; }

逻辑说明:当分支性质发生变化时,用>:root { --tree-gap: 30px; } ul.tree li { padding-top: var(--tree-gap); } ul.tree li::before, ul.tree li::after { height: var(--tree-gap); }

不要问为什么不能在两处分别填数字,我吃过亏,变量化之后这类问题一次根除。

5.2 换行断线:节点文字过长引发的撑破

现象:节点里的人名加生卒年很长,比如“某某公 配某氏 生五子”,卡片宽度撑到200px以上,导致整行节点从父ul里溢出,线条从卡片左侧伸出时被切断。

原因:flex布局下节点自然宽度超过父容器,white-space:nowrap又禁掉了换行,子项溢出,线的right:50%计算错位。

解决:要么限制卡片最大宽度并允许内部折行,要么给容器加max-width和overflow:auto。族谱页面里我一般不强制折行,优先让祖先容器横向滚动。

5.3 最底层节点没有垂直引线

现象:树的最后一层节点,它们的头顶应当有一条短竖线连接父线,但实际出不来。

原因:最底层li没有子ul,选择器ul.tree li::before依然生效了,但li:last-child的规则把它的::after给吞了,导致横向线只剩一半,垂下来的短线跟着消失。

解决:检查li:last-child::after和li:first-child::before的边界规则,单独为无子分支的节点补一条竖直短线:

ul.tree li:only-child::before { border-left: 2px solid #888; }

5.4 悬浮卡片遮挡线条

现象:鼠标移到节点上高亮,背景色变深,结果节点的下半截线条被卡片背景涂抹掉,密集区域像被橡皮擦过。

原因:节点卡片设置了不透明的背景色,原先靠z-index盖住线。悬浮状态颜色加深后,明暗反差让被盖住的半截线显形。

解决:设置节点背景时留一点透明度,或者干脆在悬浮时把线条的z-index抬到卡片上面:

ul.tree li:hover::before, ul.tree li:hover::after { z-index: 3; }

5.5 打印预览时线条大面积消失

现象:页面里显示正常,浏览器打印或导出PDF时,连线全部消失,只剩光秃秃的节点卡片。

原因:许多浏览器在打印时会简化背景和部分伪元素渲染,特别是border-top这类细线在低分辨率打印缩放下被合并掉。

解决:打印样式里强制输出颜色和线条:

@media print { * { -webkit-print-color-adjust: exact; print-color-adjust: exact; } ul.tree li::before, ul.tree li::after { border-width: 1px; } }

同时建议在导出PDF时选择“背景图形”选项,不同浏览器的默认值不一样。

6. 让静态树活起来:CSS变量主题切换与轻量折叠交互

资源包最终留了一个实用技巧:不加任何框架,靠CSS变量和一段十行以内的原生JS,让族谱树可以换主题、可以折叠。

先搭变量。把节点背景、边框、线色、字色全部抽到根选择器:

:root[data-theme="classic"] { --node-bg: #faf6ef; --node-border: #b5a07e; --node-text: #4a3a2a; --line-color: #8a7a62; } :root[data-theme="dark"] { --node-bg: #2a2a2e; --node-border: #6a6a72; --node-text: #e8e8e6; --line-color: #606060; }

使用时,在body或html上切换><li class="collapsible"> <div class="tree-node"> 三房 <button class="toggle" type="button">折叠</button> </div> <ul class="branch"> ... </ul> </li>

ul.branch { overflow: hidden; transition: max-height 0.3s ease; max-height: 1000px; } li.collapsed > ul.branch { max-height: 0; padding-top: 0; }
document.querySelectorAll(".toggle").forEach(function (btn) { btn.addEventListener("click", function () { var li = this.closest("li"); li.classList.toggle("collapsed"); this.textContent = li.classList.contains("collapsed") ? "展开" : "折叠"; }); });

逻辑说明:折叠不改变DOM结构,只是通过max-height:0收缩子分支高度,再用overflow:hidden隐藏溢出的内容。这样所有节点始终停留在文档流里,树的结构不会跳动。参数上max-height:1000px要大于展开后的实际高度,否则大分支会被截断。需要保险时可以直接用大值如max-height:10000px。

还有一个我常用的附加操作:给li增加title属性,鼠标悬停时原生显示该成员的生卒信息,不用任何前端框架就完成了信息补充。记得把长文本里的引号转义,否则title会提前截断。

整套做下来回头看,当初那个“不要图表库”的玄学需求,最后用一份干净HTML加一套可控CSS就交付了,中间唯一让我熬夜的地方是连线对不齐和打印消失这两件事,也就是第5章里那两条。从那以后我每次设计这类树形展示,都会强制自己走一遍流程:先列字段约定,再定间距变量,最后检查打印样式。这三步走完,基本不会再翻车。希望这份凭经验整理出的纯CSS族谱树形结构能帮到你,少踩那些我已经替你踩过的坑。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询