Family Tree with pure HTML and CSS (or with minimal JS)

css family tree responsive
family tree codepen
binary tree html css
hierarchical tree structure in html
css hierarchy tree
jquery family tree
horizontal tree structure in html css
org tree js

I'm trying to build a family tree with HTML and CSS. I found a good example on codepen.

As a family is not a simple node hierarchy, but sometimes rather complex relations, I need to have multiple nodes that almost act as one. But let's start with not the most complex example, taking my family as an example, starting with my grand mother from the mother side as the root:

  • There is my father who is the ex-husband of my mother
  • My mother married again, thus I have a step father

So the base node from above is my mother, but me and my sister should be placed below my father as we are not related to the step father. I tried to picture this here:

Here is my markup and css (based on the codepen example above):

/* Person */
.person {
	border: 1px solid black;
	padding: 10px;
	min-width: 150px;
	background-color: #FFFFFF;
	display: inline-block;
}

.person.female {
	border-color: #F45B69;
}

.person.male {
	border-color: #456990;
}

.person div {
	text-align: center;
}

.person .name {
	font-size: 16px;
}

.person .parentDrop, .person .spouseDrop, .person .childDrop {
	border: 1px dashed #000000;
	width: auto;
	min-width: 80px;
	min-height: 80px;
	display: inline-block;
	vertical-align: top;
	position: relative;
	padding-top: 15px;
}

.person .parentDrop>span,
.person .spouseDrop>span,
.person .childDrop>span {
	position: absolute;
	top: 2px;
	left: 2px;
	font-weight: bold;
}
.parentDrop>.person,
.spouseDrop>.person,
.childDrop>.person {
	margin-top: 20px;
}

/* Tree */
.tree ul {
	padding-top: 20px;
	position: relative;
	transition: all 0.5s;
	-webkit-transition: all 0.5s;
	-moz-transition: all 0.5s;
}

.tree li {
	display: table-cell;
	text-align: center;
	list-style-type: none;
	position: relative;
	padding: 20px 5px 0 5px;
	transition: all 0.5s;
	-webkit-transition: all 0.5s;
	-moz-transition: all 0.5s;
}



/*We will use ::before and ::after to draw the connectors*/
.tree li::before, .tree li::after {
	content: '';
	position: absolute;
	top: 0;
	right: 50%;
	border-top: 1px solid #ccc;
	width: 50%;
	height: 20px;
}

.tree li::after {
	right: auto;
	left: 50%;
	border-left: 1px solid #ccc;
}

/*We need to remove left-right connectors from elements without 
any siblings*/
.tree li:only-child::after, .tree li:only-child::before {
	display: none;
}

/*Remove space from the top of single children*/
.tree li:only-child {
	padding-top: 0;
}

/*Remove left connector from first child and 
right connector from last child*/
.tree li:first-child::before, .tree li:last-child::after {
	border: 0 none;
}
/*Adding back the vertical connector to the last nodes*/
.tree li:last-child::before {
	border-right: 1px solid #ccc;
	border-radius: 0 5px 0 0;
	-webkit-border-radius: 0 5px 0 0;
	-moz-border-radius: 0 5px 0 0;
}

.tree li:first-child::after {
	border-radius: 5px 0 0 0;
	-webkit-border-radius: 5px 0 0 0;
	-moz-border-radius: 5px 0 0 0;
}

/*Time to add downward connectors from parents*/
.tree ul ul::before {
	content: '';
	position: absolute;
	top: 0;
	left: 50%;
	border-left: 1px solid #ccc;
	width: 0;
	height: 20px;
}

.tree li .parent {
	transition: all 0.5s;
	-webkit-transition: all 0.5s;
	-moz-transition: all 0.5s;
	margin-top: 10px;
}
.tree li .parent::before {
    content: '';
    position: absolute;
    top: 40px;
    left: 50%;
    border-left: 1px solid #ccc;
    border-right: 1px solid #ccc;
    width: 3px;
    height: 10px;
}
.tree li .family {
	position: relative;
}
.tree li .family .spouse {
	position: absolute;
	top: 0;
	left: 50%;
    margin-left: 95px;
}
.tree li .family .spouse::before {
    content: '';
    position: absolute;
    top: 50%;
    left: -10px;
    border-top: 1px solid #ccc;
    border-bottom: 1px solid #ccc;
    width: 10px;
    height: 3px;
}

/*Time for some hover effects*/
/*We will apply the hover effect the the lineage of the element also*/
.tree li .child:hover,
.tree li .child:hover+.parent .person,
.tree li .parent .person:hover,
.tree li .child:hover+.parent .person+ul li .child,
.tree li .parent .person:hover+ul li .child,
.tree li .child:hover+.parent .person+ul li .parent .person,
.tree li .parent .person:hover+ul li .parent .person {
	background: #c8e4f8;
	color: #000;
	border: 1px solid #94a0b4;
}
/*Connector styles on hover*/
.tree li .child:hover+.parent::before,
.tree li .child:hover+.parent .person+ul li::after,
.tree li .parent .person:hover+ul li::after,
.tree li .child:hover+.parent .person+ul li::before,
.tree li .parent .person:hover+ul li::before,
.tree li .child:hover+.parent .person+ul::before,
.tree li .parent .person:hover+ul::before,
.tree li .child:hover+.parent .person+ul ul::before,
.tree li .parent .person:hover+ul ul::before {
	border-color: #94a0b4;
}
<div class="tree">
<ul>
<li>
	<div class="family">
		<div class="person child male">
			<div class="name">Grandfather</div>
		</div>
    <div class="parent">
      <div class="person female">
        <div class="name">Grandmother</div>
      </div>
      <ul>
        <li>
          <div class="family" style="width: 172px">
            <div class="person child male">
              <div class="name">Uncle</div>
            </div>
            <div class="parent">
              <div class="person female">
                <div class="name">Wife of Uncle</div>
              </div>
            </div>
          </div>
        </li>
        <li>
          <div class="family" style="width: 172px">
            <div class="person child female">
              <div class="name">Aunt</div>
            </div>
            <div class="parent">
              <div class="person male">
                <div class="name">Husband of Aunt</div>
              </div>
            </div>
          </div>
        </li>
        <li>
          <div class="family" style="width: 344px">
            <div class="person child female">
              <div class="name">Mother</div>
            </div>
            <div class="parent">
              <div class="person male">
                <div class="name">Father</div>
              </div>
              <ul>
                <li>
                  <div class="person child male">
                    <div class="name">Me</div>
                  </div>
                </li>
                <li>
                  <div class="person child female">
                    <div class="name">Sister</div>
                  </div>
                </li>
              </ul>
            </div>
            <div class="person spouse male">
              <div class="name">Spouse</div>
            </div>
          </div>
        </li>
      </ul>
    </div>
	</div>
</li>
</ul>
</div>

I would suggest you to use some third party js family tree library

For Example OrgChart JS

Family tree algorithm could be very complex this is why it is easier if multiple nodes act as one, as you said.

The only thing is that you have to learn how to implement your own template in OrgChart JS.

Here is an example with British Royal Family Tree:

window.onload = function () {
    OrgChart.templates.family_template_11 = Object.assign({}, OrgChart.templates.ana);
    OrgChart.templates.family_template_11.size = [200, 140];
    OrgChart.templates.family_template_11.plus = "";
    OrgChart.templates.family_template_11.minus = "";
    OrgChart.templates.family_template_11.node = '';
    OrgChart.templates.family_template_11.rippleRadius = 45;
    OrgChart.templates.family_template_11.name_1 = '<text class="name_1" style="font-size: 12px;" fill="#000000" x="100" y="105" text-anchor="middle">{val}</text>';
    OrgChart.templates.family_template_11.name_2 = '<text class="name_2" style="font-size: 12px;" fill="#000000" x="235" y="105" text-anchor="middle">{val}</text>';
    OrgChart.templates.family_template_11.name_3 = '<text class="name_3" style="font-size: 12px;" fill="#000000" x="370" y="105" text-anchor="middle">{val}</text>';
    OrgChart.templates.family_template_11.title_1 = '<text class="title_1" style="font-size: 12px;" fill="#aeaeae" x="100" y="120" text-anchor="middle">{val}</text>';
    OrgChart.templates.family_template_11.title_2 = '<text class="title_2" style="font-size: 12px;" fill="#aeaeae" x="235" y="120" text-anchor="middle">{val}</text>';
    OrgChart.templates.family_template_11.title_3 = '<text class="title_3" style="font-size: 12px;" fill="#aeaeae" x="370" y="120" text-anchor="middle">{val}</text>';
    OrgChart.templates.family_template_11.img_0 = '<clipPath id="{randId}"><circle cx="100" cy="45" r="40"></circle></clipPath><circle stroke-width="3" fill="none" stroke="#aeaeae" cx="100" cy="45" r="45"></circle><image preserveAspectRatio="xMidYMid slice" clip-path="url(#{randId})" xlink:href="{val}" x="60" y="5"  width="80" height="80"></image>';
    OrgChart.templates.family_template_11.linkAdjuster =
        {
            fromX: 0,
            fromY: 0,
            toX: 0,
            toY: 0
        };


    OrgChart.templates.family_template_12 = Object.assign({}, OrgChart.templates.family_template_11);
    OrgChart.templates.family_template_12.img_0 = '<clipPath id="{randId}"><circle cx="100" cy="45" r="40"></circle></clipPath><circle stroke-width="3" fill="none" stroke="#039BE5" cx="100" cy="45" r="45"></circle><image preserveAspectRatio="xMidYMid slice" clip-path="url(#{randId})" xlink:href="{val}" x="60" y="5"  width="80" height="80"></image>';
    OrgChart.templates.family_template_12.linkAdjuster =
        {
            fromX: 0,
            fromY: 0,
            toX: 0,
            toY: -95
        };



    OrgChart.templates.family_template_21 = Object.assign({}, OrgChart.templates.family_template_11);
    OrgChart.templates.family_template_21.size = [335, 140];
    OrgChart.templates.family_template_21.node = '<line x1="145" x2="190" y1="45" y2="45" stroke-width="1" stroke="#000000"></line>';
    OrgChart.templates.family_template_21.img_1 = '<clipPath id="{randId}"><circle cx="235" cy="45" r="40"></circle></clipPath><circle stroke-width="3" fill="none" stroke="#aeaeae" cx="235" cy="45" r="45"></circle><image preserveAspectRatio="xMidYMid slice" clip-path="url(#{randId})" xlink:href="{val}" x="195" y="5"  width="80" height="80"></image>';
    OrgChart.templates.family_template_21.linkAdjuster =
        {
            fromX: 65,
            fromY: 0,
            toX: 0,
            toY: -95
        };

    OrgChart.templates.family_template_22 = Object.assign({}, OrgChart.templates.family_template_21);
    OrgChart.templates.family_template_22.linkAdjuster =
        {
            fromX: -70,
            fromY: 0,
            toX: 65,
            toY: -95
        };

    OrgChart.templates.family_template_23 = Object.assign({}, OrgChart.templates.family_template_21);
    OrgChart.templates.family_template_23.img_1 = '<clipPath id="{randId}"><circle cx="235" cy="45" r="40"></circle></clipPath><circle stroke-width="3" fill="none" stroke="#039BE5" cx="235" cy="45" r="45"></circle><image preserveAspectRatio="xMidYMid slice" clip-path="url(#{randId})" xlink:href="{val}" x="195" y="5"  width="80" height="80"></image>';
    OrgChart.templates.family_template_23.linkAdjuster =
        {
            fromX: 65,
            fromY: 0,
            toX: 65,
            toY: -95
        };

    OrgChart.templates.family_template_24 = Object.assign({}, OrgChart.templates.family_template_21);
    OrgChart.templates.family_template_24.img_0 = '<clipPath id="{randId}"><circle cx="100" cy="45" r="40"></circle></clipPath><circle stroke-width="3" fill="none" stroke="#039BE5" cx="100" cy="45" r="45"></circle><image preserveAspectRatio="xMidYMid slice" clip-path="url(#{randId})" xlink:href="{val}" x="60" y="5"  width="80" height="80"></image>';


    OrgChart.templates.family_template_25 = Object.assign({}, OrgChart.templates.family_template_21);
    OrgChart.templates.family_template_25.img_1 = '<clipPath id="{randId}"><circle cx="235" cy="45" r="40"></circle></clipPath><circle stroke-width="3" fill="none" stroke="#039BE5" cx="235" cy="45" r="45"></circle><image preserveAspectRatio="xMidYMid slice" clip-path="url(#{randId})" xlink:href="{val}" x="195" y="5"  width="80" height="80"></image>';




    OrgChart.templates.family_template_31 = Object.assign({}, OrgChart.templates.family_template_21);
    OrgChart.templates.family_template_31.size = [470, 140];
    OrgChart.templates.family_template_31.node = '<line x1="145" x2="190" y1="45" y2="45" stroke-width="1" stroke="#000000"></line><line x1="280" x2="325" y1="45" y2="45" stroke-width="1" stroke="#F57C00"></line>';
    OrgChart.templates.family_template_31.img_1 = '<clipPath id="{randId}"><circle cx="235" cy="45" r="40"></circle></clipPath><circle stroke-width="3" fill="none" stroke="#039BE5" cx="235" cy="45" r="45"></circle><image preserveAspectRatio="xMidYMid slice" clip-path="url(#{randId})" xlink:href="{val}" x="195" y="5"  width="80" height="80"></image>';

    OrgChart.templates.family_template_31.img_2 = '<clipPath id="{randId}"><circle cx="370" cy="45" r="40"></circle></clipPath><circle stroke-width="3" fill="none" stroke="#aeaeae" cx="370" cy="45" r="45"></circle><image preserveAspectRatio="xMidYMid slice" clip-path="url(#{randId})" xlink:href="{val}" x="330" y="5"  width="80" height="80"></image>';
    OrgChart.templates.family_template_31.linkAdjuster =
        {
            fromX: 0,
            fromY: 0,
            toX: 0,
            toY: -95
        };

    var chart = new OrgChart(document.getElementById("tree"), {
        tags: {
            "family_template_11": {
                template: "family_template_11"
            },
            "family_template_21": {
                template: "family_template_21"
            },
            "family_template_31": {
                template: "family_template_31"
            },
            "family_template_22": {
                template: "family_template_22"
            },
            "family_template_23": {
                template: "family_template_23"
            },
            "family_template_24": {
                template: "family_template_24"
            },
            "family_template_25": {
                template: "family_template_25"
            },
            "family_template_12": {
                template: "family_template_12"
            }
        },
        enableSearch: false,
        nodeMouseClickBehaviour: BALKANGraph.action.none,
        mouseScroolBehaviour: BALKANGraph.action.zoom,
        scaleInitial: BALKANGraph.match.boundary,
        nodeBinding: {
            name_1: "name1",
            name_2: "name2",
            name_3: "name3",
            title_1: "title1",
            title_2: "title2",
            title_3: "title3",
            img_0: "img0",
            img_1: "img1",
            img_2: "img2"
        },
        links: [
            { from: "2", to: "1" },
            { from: "3", to: "1" },
            { from: "4", to: "2" },
            { from: "5", to: "2" },
            { from: "6", to: "2" },
            { from: "7", to: "2" },
            { from: "8", to: "4" },
            { from: "9", to: "4" },
            { from: "10", to: "8" },
            { from: "11", to: "8" },
            { from: "12", to: "8" },
        ],
        nodes: [
            { id: "1", tags: ["family_template_24"], name1: "King George VI", name2: "Queen Elizabeth,", title2: "The Queen Mother", img0: "https://balkangraph.com/js/img/f1.png", img1: "https://balkangraph.com/js/img/f2.png" },
            { id: "2", tags: ["family_template_25"], name1: "Prince Philip", name2: "Queen Elizabeth II", title1: "Duke of Edinburgh", img0: "https://balkangraph.com/js/img/f3.png", img1: "https://balkangraph.com/js/img/f5.png" },
            { id: "3", tags: ["family_template_11"], name1: "Princess Margaret", img0: "https://balkangraph.com/js/img/f6.png" },
            { id: "4", tags: ["family_template_31"], name1: "Camila,", name2: "Charles,", name3: "Diana,", title1: "Duchess of Cornwall", title2: "Prince of Wales", title3: "Princess of Wales", img0: "https://balkangraph.com/js/img/f7.png", img1: "https://balkangraph.com/js/img/f8.png", img2: "https://balkangraph.com/js/img/f9.png" },
            { id: "5", tags: ["family_template_11"], name1: "Anne", title1: "Princess Royal", img0: "https://balkangraph.com/js/img/f10.png" },
            { id: "6", tags: ["family_template_11"], name1: "Prince Andrew", title1: "Duke of York", img0: "https://balkangraph.com/js/img/f11.png" },
            { id: "7", tags: ["family_template_11"], name1: "Prince Edward", title1: "Earl of Wessex", img0: "https://balkangraph.com/js/img/f12.png" },
            { id: "8", tags: ["family_template_23"], name1: "Catherine,", name2: "Prince William", title1: "Duchess of Cambridge", title2: "Duch of Cambridge", img0: "https://balkangraph.com/js/img/f13.png", img1: "https://balkangraph.com/js/img/f14.png" },
            { id: "9", tags: ["family_template_22"], name1: "Prince Harry", name2: "Meghan Markle", img0: "https://balkangraph.com/js/img/f15.png", img1: "https://balkangraph.com/js/img/f16.png" },
            { id: "10", tags: ["family_template_12"], name1: "Prince George of Cambridge", img0: "https://balkangraph.com/js/img/f17.png" },
            { id: "11", tags: ["family_template_12"], name1: "Prince Charlotte of Cambridge", img0: "https://balkangraph.com/js/img/f18.png" },
            { id: "12", tags: ["family_template_12"], name1: "Prince Louis of Cambridge", img0: "https://balkangraph.com/js/img/f19.png" }
        ]
    });
};
html, body {
    margin: 0px;
    padding: 0px;
    width: 100%;
    height: 100%;
    font-family: Helvetica;
    overflow: hidden;
}

#tree {
    width: 100%;
    height: 100%;
}
<script src="https://balkangraph.com/js/latest/OrgChart.js"></script>

<div id="tree"></div>

Best Free Family Tree In JavaScript & CSS, JavaScript & CSS Family Tree. Pretty Clean Tree Diagram In Pure CSS. Category​: Chart A Pure CSS solution to generate a pretty clean tree diagram from nested HTML lists. Minimal Image Parallax Scroll Effect In JavaScript – Parallax.js. A Pure CSS solution to generate a pretty clean tree diagram from nested HTML lists. Demo Download Tags: Family Tree , Organization Chart , tree view Render Family/Organization Tree From JSON – treeMaker


Have you thought of using SVG, or SVG+HTML, in conjunction with your CSS? The tool described at SVG Family-Tree Generator is merely a designer that spits out a configurable combination of HTML, SVG, JavaScript, and CSS. Its visual tree has facilities for showing tentative parentage, unshown children (to indicate there are others, but irrelevant to the publication the tree is embedded within), sequential marriages, and CSS customisation of the joining lines. There are zero-script configurations, and all the output is non-obfuscated (allowing editing or learning).

If you want to avoid script then you may find SVG to be something to avoid also. Although it is hugely powerful, and even allows interaction of the user with the box elements, some sites don't like it; notably WordPress where you might be better converting the SVG to a PNG or similar.

Vertical Family Tree in Pure CSS – CodeMyUI, Ever wanted to create a flowchart or family tree in pure CSS? You have come to the right place, This snippet creates a multilevel family tree and when you. A Pure CSS solution to generate a pretty clean tree diagram from nested HTML lists. Useful for hierarchical tree structure such as Family Tree, Organization Chart, etc. How to use it: 1. The HTML structure for the tree diagram.


Here is something to get you started, I have to leave it at this now.

.tree {
  display:flex;
   width:100%;
  justify-content:center;
  flex-direction:column;
}

.tree > div {
  display:flex;
  justify-content:center;
  width:auto;
/*   background:indianred; */
  align-self:center;
}

.tree div > div {
  
  margin:1em;
}

.spouse::before {
  content:" ";
  position:absolute;
  margin-top:1.5em;
  margin-left:-2.1em;
  width:2em;
  height:1px;
  align-self:center;
  border-top:2px solid purple;
}

.paternal::after {
  content:" ";
  position:absolute;
  margin-top:2px;
  margin-left:3em;
  width:1px;
  height:2em;
  align-self:center;
  border-left:2px solid purple;
}

.person {
  border:2px solid pink;
}

.person {
  border:2px solid pink;
}
<div class="tree">
	<div class="generationone">
		<div class="person male grandparent">
			<div class="name">Grandfather</div>
		</div>
    	<div class="person spouse female grandparent">
		    <div class="name">Grandmother</div>
	   </div>
  </div><!-- end one -->
  <div class="generationonetwo">
		<div class="person male child">
				<div class="name">Uncle</div>
		</div>
    <div class="person spouse female">
			 <div class="name">Wife of Uncle</div>
		 </div>
		<div class="person child female">
			<div class="name">Aunt</div>
		</div>
		<div class="person spouse male">
				<div class="name">Husband of Aunt</div>
		</div>
		<div class="person paternal male">
				<div class="name">Father</div>
		</div>
		<div class="person spouse child female">
				<div class="name">Mother</div>
		</div>
		<div class="person spouse male">
				<div class="name">Step Father</div>
		</div>
  </div><!-- end two -->
  <div class="generationonetthree">
		<div class="person child male">
				<div class="name">Me</div>
		</div>
    <div class="person child female">
         <div class="name">Sister</div>
    </div>
		<div class="person spouse male">
				<div class="name">Spouse</div>
		</div>
	</div><!-- end three -->
</div><!-- end tree -->

Example Pure CSS Family Tree Markup – Justin Cooney, and I'm posting below some pure CSS/HTML markup that I found on stack overflow To deal with this issue, you can add some JavaScript to dynamically set the There is no clean way that I've found yet that can determine the Here is the markup that will render the example family tree shown in the  A minimalist pure CSS tree view for presenting hierarchical data in a tree structure. ← Minimal 12 Column CSS/SCSS Grid Layout html-duration-picker.js.


Here is my css/html/jQuery family tree solution that builds the tree to the right of the base person/couple. People are connected to their parents by connectors which are created using some png files (not included) that measure 2px high and 10 px wide. centerPixels has the center 4 filled in and pointRight and pointLeft do just that by filling in their left or right halves. It's not perfect, but it works with the sample data provided.

<!DOCTYPE html>
<head>
    <meta charset="utf-8">
    <script src="/lib/jquery-3.3.1.min.js"></script>
    <style>
    .table_Node {border-collapse:collapse; border-spacing:0px; border:0px; padding:0px; vertical-align:middle;}
    .th_Node {border:0px; padding:0px; padding-left:4px; vertical-align:middle;}
    .td_Node {border:0px; padding:5px; vertical-align:middle; position:relative;}
    .lbl_NodeName {font-size:14px;}
    .lbl_NodeBirthDeath {font-size:12px;}
    .divPerson {    
        display:block; 
        width:100%; 
        color:#eeeeee; 
        border:1px solid black; 
        padding:3px 4px 5px 4px; 
        left:8px; 
        min-width:75px; 
        border-radius:8px; 
        background-color: #333;
    }
    .div_AnchorLine {
        display:block;
        position:absolute;
        width:10px;
        left:0px;
        background-image: url("/img/centerPixels.png");
    }
    .div_AnchorTop {
        display:block;
        position:absolute;
        width:10px;
        height:2px;
        top:0px;
        background-image: url("/img/pointRight.png");
    }
    .div_AnchorBottom {
        display:block;
        position:absolute;
        width:10px;
        height:2px;
        bottom:0px;
        background-image: url("/img/pointRight.png");
    }
    .table_EmptyParent {
        height:28px;
        vertical-align:middle;
    }

    .table_Dad {
        margin-left:10px; 
        margin-bottom:2px;
    }

    .div_DadLine {
        display:block;
        position:absolute;
        width:10px;
        left:9px;
        background-image: url("/img/centerPixels.png");
    }
    .div_DadTop {
        display:block;
        position:absolute;
        width:10px;
        height:2px;
        top:0px;
        background-image: url("/img/pointRight.png");
    }
    .div_DadBottom {
        display:block;
        position:absolute;
        width:10px;
        height:2px;
        bottom:0px;
        background-image: url("/img/pointLeft.png");
    }

    .table_Mom {
        margin-left:10px; 
        margin-top:2px;
    }
    .div_MomLine {
        display:block;
        position:absolute;
        width:10px;
        /*height:25%;
        top:50%;*/
        left:9px;
        background-image: url("/img/centerPixels.png");
    }
    .div_MomTop {
        display:block;
        position:absolute;
        width:10px;
        height:2px;
        top:0px;
        background-image: url("/img/pointLeft.png");
    }
    .div_MomBottom {
        display:block;
        position:absolute;
        width:10px;
        height:2px;
        bottom:0px;
        background-image: url("/img/pointRight.png");
    }

    </style>
</head>
<body>
<div id='treeDiv' style='position:relative;'></div>
</body>
</html>

<script>

// this is just skeleton objects
var people = [];
var blankPerson = {
    ID : 0,
    Name:"&nbsp;",
    BirthDate:"",
    BirthPlace:"",
    DeathDate:"",
    DeathPlace:"",
    DadID:0,
    MomID:0
};
var marriages = [];
var blankMarriage = {
    HusbandID: 0, 
    WifeID: 0,
    MarriedDate: "",
    MarriedPlace: ""
};
// these anchors are what are used to start the tree
// the anchorPerson is the person with no children
// if the tree data is broken (i.e. a great-grandparent without a chlid), 
// then results are unpredictable.
var anchorPerson = blankPerson;
var anchorHusband = blankPerson;
var anchorWife = blankPerson;
var anchorMarriage = blankMarriage;

$(document).ready(function(){
    loadPeople();
    loadMarriages();
    buildTree();
});

// this should be an ajax get
function loadPeople()
{
    addPerson(1100, "Person1100","1890-01-01","1921-07-14",0,0);
    addPerson(10, "Person10","","",100,101);
    addPerson(100, "Person100","","",1000,2);
    addPerson(1001, "Person1001","","",0,1100);
    addPerson(1, "Person1","1992-01-15","",10,11);
    addPerson(101, "Person101","","",1010,1011);
    addPerson(1010, "Person1010","","",0,0);
    addPerson(1011, "Person1011","","",0,0);

    addPerson(11, "Person11","","",110,111);
    addPerson(110, "Person110","","",1100,0);
    addPerson(1000, "Person1000","","",0,0);
    addPerson(8, "Person8","","",0,0);
    addPerson(2, "Person2","","",0,21);
    addPerson(1101, "Person1101","","",0,0);
    addPerson(21, "Person21","","",1101,0);
    addPerson(111, "Person111","","",1110,1111);
    addPerson(1110, "Person1110","","",0,0);
    addPerson(1111, "Person1111","","",0,0);
}

// the ajax routine will return an array of person objects,
// so this won't even be here
function addPerson(id,name,born,died,dadid, momid)
{
    people.push({
        ID : id,
        Name:name,
        BirthDate:born,
        BirthPlace:"",
        DeathDate:died,
        DeathPlace:"",
        DadID:dadid,
        MomID:momid
    });
}

// this should be an ajax get
function loadMarriages()
{
    addMarriage(110,111);
    addMarriage(1110,1111);
    addMarriage(1,8);
    addMarriage(10,11);
    addMarriage(100,101);
    addMarriage(0,21);
    addMarriage(1000,2);
    addMarriage(1100,1101);
}

// the ajax routine will return an array of person objects,
// so this won't even be here
function addMarriage(hid,wid)
{
    marriages.push({
        HusbandID: hid, 
        WifeID: wid,
        MarriedDate: "",
        MarriedPlace: ""
    });    
}

// pretty much the only thing I couldn't figure out 
// is how to always get someone centered on parents
// people can ride a little high or a little low on their connectors 
// line depending on how the tree is structured to their right
function buildTree()
{
    // clear everything
    $('#treeDiv').html("");

    // find someone with no children
    // again, if the tree is broken, then this will not work
    anchorPerson = findAnchorPerson();

    // find out if the anchorPerson is married
    anchorMarriage = findMarriage(anchorPerson.ID);   
    if (anchorMarriage == blankMarriage)
    {
        // if not, start the tree with the anchorPerson
        $('#treeDiv').append("<table id='table_"+anchorPerson.ID+"' class='table_Node' cellpadding='0'></table>");
    }
    else
    {
        // otherwise, figure out who is the husband and who is the wife and then start with both
        anchorHusband = findPersonByID(anchorMarriage.HusbandID);
        anchorWife = findPersonByID(anchorMarriage.WifeID);
        $('#treeDiv').append("<table id='table_"+anchorMarriage.HusbandID+"' cellpadding='0' class='table_Node' style='margin-left:5px;'></table>");
        $('#treeDiv').append("<div id='div_AnchorLine' class='div_AnchorLine' style='top:0px;'>"
                                +"<div class='div_AnchorTop'></div>"        
                                +"<div class='div_AnchorBottom'></div>"
                            +"</div>");
        $('#treeDiv').append("<table id='table_"+anchorMarriage.WifeID+"' cellpadding='0' class='table_Node' style='margin-left:5px;'></table>");

    }

    var finished = false;
    while(finished == false)
    {
        var somethingWasDone = false;
        $.each(people,function(){
            var thisPerson = this;
            var foundIt = false;
            $('.table_Node').each(function(){
                if ($(this).attr('id') == "table_"+thisPerson.ID)
                {
                    if ($(this).html() == "") //  should only happen twice. once with anchorHusband and once with anchorWife
                    {
                        somethingWasDone = true;
                        $(this).html(getPersonHTML(thisPerson));                        
                    }
                    else if ($('#div_'+thisPerson.ID).html() == "&nbsp;")
                    {
                        somethingWasDone = true;
                        $(this).html(getPersonHTML(thisPerson));                        
                    }
                }
            });
        });
        if (somethingWasDone == false)
            finished = true;
    }
    // if the anchor person is married, add the line linking the couple
    if (anchorMarriage != blankMarriage)
    {
        var top = $('#div_'+anchorMarriage.HusbandID).offset().top;
        var height = Number($('#div_'+anchorMarriage.WifeID).offset().top) - top;
        $('#div_AnchorLine').attr('style','top:'+Number(top+8)+'px; height:'+height+'px;');
    }

    // now, add connectots to everybody's parents
    $('.td_Parents').each(function(){
        addDadConnector($(this).attr('id').substr(11));
        addMomConnector($(this).attr('id').substr(11));
    });
}

function getPersonHTML(person)
{
    var dad = findPersonByID(person.DadID);
    var mom = findPersonByID(person.MomID);

    var dadClass = "table_Dad";
    var momClass = "table_Mom";

    if (dad == blankPerson && mom != blankPerson)
        dadClass += " table_EmptyParent";

    if (mom == blankPerson && dad != blankPerson)
        momClass += " table_EmptyParent";

    var HTML = ""
            +"<tr id='tr_"+person.ID+"' class='tr_Node'>"
                +"<th class='th_Node'>"
                    +"<div id='div_"+person.ID+"' class='divPerson'>"
                        +"<label class='lbl_NodeName'>"+person.Name+"</label>"
                        +"<label class='lbl_NodeBirthDeath'>"+getBirthDeathInfo(person)+"</label>"
                        +"</div>"
                +"</th>"
                +"<td id='td_parents_"+person.ID+"' class='td_Node td_Parents'>"
                    +"<table id='table_"+person.DadID+"' class='table_Node "+dadClass+"'>"
                        +"<tr id='tr_"+person.DadID+"' class='tr_Node'>"
                            +"<th class='th_Node'>"
                                +"<div id='div_"+person.DadID+"' class='divPerson'>&nbsp;</div>"
                            +"</th>"
                        +"</tr>"
                    +"</table>"
                    +"<table id='table_"+person.MomID+"' class='table_Node "+momClass+"' style='height:50%'>"
                        +"<tr id='tr_"+person.MomID+"' class='tr_Node'>"
                            +"<th class='th_Node'>"
                                +"<div id='div_"+person.MomID+"' class='divPerson'>&nbsp;</div>"
                            +"</th>"
                        +"</tr>"
                    +"</table>"
                +"</td>"
            +"</tr>"
    return HTML;
}
// the goal here is to return "(1900-1975)"
// but, we may get "(-1975)" or "(1900-)" or nothing at all
// the div/label arrangement allows for either with no impact on spacing
function getBirthDeathInfo(person)
{
    var birthyear = getBirthYear(person);
    var deathyear = geDeathYear(person);
    var retval = birthyear+" - "+deathyear;
    if (retval == " - ")
        retval = "";
    else
        retval = "<br/>("+retval+")";
    return retval;
}
function getBirthYear(person)
{
    var retval = "";
    if (person.BirthDate.length >= 4)
        retval = person.BirthDate.substr(0,4);
    return retval;
}

function geDeathYear(person)
{
    var retval = "";
    if (person.DeathDate.length >= 4)
        retval = person.DeathDate.substr(0,4);
    return retval;
}

// add the connector between the person and the dad
function addDadConnector(personID)
{
    if ($('#td_parents_'+personID).length != 0)
    {
        var personTop = Number($('#div_'+personID).offset().top);
        var personHeight = Number($('#div_'+personID).outerHeight());
        var personPadding = parseInt($('#div_'+personID).css('padding-top'))+parseInt($('#div_'+personID).css('padding-bottom'));
        var personCenter = personTop + ((personHeight + personPadding) / 2) - 10;
        var dadTable = $('#td_parents_'+personID).children('.table_Node')[0];
        var dadDiv = $(dadTable).find('.divPerson')[0];
        if ($(dadDiv).length > 0)
        {
            var dadTop = Number($(dadDiv).offset().top);
            var dadHeight = Number($(dadDiv).outerHeight());
            var dadPadding = parseInt($(dadDiv).css('padding-top'))+parseInt($(dadDiv).css('padding-bottom'));
            var dadCenter = dadTop + ((dadHeight + dadPadding) / 2) - 10;
            var left = Number($(dadDiv).offset().left) - 18;
            var height = personCenter - dadCenter;
            $('#treeDiv').append("<div id='div_DadLine_"+personID+"' class='div_DadLine' style='top:"+dadCenter+"px; left:"+left+"px; height:"+height+"px;'>"
                                    +"<div class='div_DadTop'></div>"
                                    +"<div class='div_DadBottom'></div>"
                                +"</div>");
        }
    }
}

// add the connector between the person and the mom
function addMomConnector(personID)
{
    if ($('#td_parents_'+personID).length != 0)
    {
        var personTop = Number($('#div_'+personID).offset().top);
        var personHeight = Number($('#div_'+personID).innerHeight());
        var personPadding = parseInt($('#div_'+personID).css('padding-top'))+parseInt($('#div_'+personID).css('padding-bottom'));
        var personCenter = personTop + ((personHeight + personPadding) / 2) - 11;
        var momTable = $('#td_parents_'+personID).children('.table_Node')[1];            
        var momDiv = $(momTable).find('.divPerson')[0];
        if ($(momDiv).length > 0)
        {
            var momTop = Number($(momDiv).offset().top);
            var momHeight = Number($(momDiv).outerHeight());
            var momPadding = parseInt($(momDiv).css('padding-top'))+parseInt($(momDiv).css('padding-bottom'));
            var momCenter = momTop + ((momHeight + momPadding) / 2) - 10;
            var left = Number($(momDiv).offset().left) - 18;
            var height = momCenter - personCenter;
            $('#treeDiv').append("<div id='div_MomLine_"+personID+"' class='div_MomLine' style='top:"+personCenter+"px; left:"+left+"px; height:"+height+"px;'>"
                                    +"<div class='div_MomTop'></div>"
                                    +"<div class='div_MomBottom'></div>"
                                +"</div>");
        }
    }
}

// find someone who has no child
// this actually returns the last on in the list with no child,
// but there should be only a max of 2 and they'd be married,
// so it doesn't really matter
// handling siblings would bring a new level of complexity
function findAnchorPerson()
{
    var thisPerson = blankPerson;
    var idWithNoChild = 0;
    $.each(people,function(){
        var somePerson = this;
        var hasChild = false;
        $.each(people,function(){
            if (this.DadID == somePerson.ID || this.MomID == somePerson.ID)
                hasChild = true;
        });
        if (hasChild == false)
            thisPerson = somePerson;
    });
    return thisPerson;
}

function findMarriage(personID)
{
    var marriage = blankMarriage;
    $.each(marriages,function(){
        if (personID == this.HusbandID || personID == this.WifeID)
             marriage = this;
    });
    return marriage;
}

function findPersonByID(personID)
{
    var person = blankPerson;
    $.each(people,function(){
        if (this.ID == personID)
            person = this;
    });
    return person;
}

// this isn't in use right now, but it might be useful at some point
function findSpouse(personID)
{
    var spouse = blankPerson;
    $.each(marriages,function(){
        if (this.HusbandID == personID)
            spouse = findPersonByID(this.WifeID)
        else if (this.WifeID == personID)
            spouse = findPersonByID(this.HusbandID)
    });
    return spouse;
}

</script>

CSS3 Family Tree, Learn to display organizational data or a family tree just using CSS, without any flash or javascript. Very simple markup - just nested lists. Pseudo elements are  Vertical Family Tree in Pure CSS – CodeMyUI. Ever wanted to create a flowchart or family tree in pure CSS? You have come to the right place, This snippet creates a multilevel family tree and when you hover.


Drawing Family Trees With JavaScript, A family tree (also called a pedigree chart) is a diagram representing family relationships. yFiles for HTML comes with a Family Tree Sample Application. fidelity and ease of development of SVG in conjunction with CSS styling, animations, as a pure JavaScript library that requires ECMAScript 5 at minimum at runtime. Visualize Hierarchical Tree Structures Using D3.js – d3-mitch-tree Pure CSS3 Html List Based Column/Bar Chart Create A Family / Organization Tree Using JavaScript and Canvas – ftree.js


How To Create a Tree View, Well organized and easy to understand Web building tutorials with lots of examples of how to use HTML, CSS, JavaScript, SQL, PHP, Python, Bootstrap, Java  This tutorial is a step by step guide that helps you in the process of creating a compelling collapsible tree menu. It explains how to add layers for creating a multi-level nested tree. It counts with beautiful minimal folders and files’ icons, and it’s very easy to catch. The tutorial helps you create the tree,


CSS Family Tree, Analyze HTML; Maximize HTML Editor; Minimize HTML Editor; Fold All; Unfold All. 42. 1. <!--. 2. We will create a family tree using just CSS(3). 3. The markup will​  I'm trying to make a family tree-style list in HTML. It needs to start at the top of the page, then spread out as it goes down the page. It needs to be a flexible layout, i.e it's not always going to get wider on the next level down. I've tried using tables, then setting […]