Articles | Family Trees |
back
Introduction
This article will show how you create a dynamic Family
Tree that you can navigate around. It uses arrays to hold the data to be
displayed in the Family Tree. The example included uses the British Royal Family
to demonstrate the varying amounts of information that can be held.
The Family Tree uses frames to show a tree structure in
one frame, plus a dynamically changing detail in the other.
The Frame Contents
Three files are required for the Family Tree, tree.htm
which contains the JavaScript and the frameset definition, topFrame.htm
and botFrame.htm which make up the contents of the two frames topFrame
and botFrame.
The contents of topFrame.htm is fairly simple.
The JavaScript function updateDetails() which appears at first sight
not to be used, and the writing to the document of the results from the parent
documents (i.e. the tree.htm file) showTree() function which
is passed the parent frames personToShow< variable.
<BODY BGCOLOR="papayawhip" TEXT="black" LINK="black" ALINK="black"
VLINK="black">
<SCRIPT LANGUAGE="JavaScript"><!--
function updateDetails(index) {
if (index != parent.details) {
parent.botFrame.document.open();
parent.botFrame.document.write(parent.PersonArray[index].details);
parent.botFrame.document.close();
parent.details = index;
}
}
document.write(parent.showTree(parent.PersonToShow));
//--></SCRIPT>
</BODY>
|
The unused updateDetails() function will, when
used, update the contents of the botFrame using the documents open(),
write() and close() methods. The data written to the screen
will be the details property of parent frames personArray[]
arrays index entry. Once the frame contents have been replaced the details
variable in the parent frame is updated with the passed index.
The writing of the details to the botframe
will not occur if the frame already holds the details, by comparing the passed index
with the details variable held in the parent frame.
The contents of the botFrame.htm file are even
simpler. The data written to the screen will be the details property of
parent frames personArray[] arrays PersonToShow index
entry held in the parent frame. This is similar to the functionality
provided by the updateDetails() function in the topFrame.
<SCRIPT LANGUAGE="JavaScript"><!--
document.write(parent.PersonArray[parent.PersonToShow].details);
//--></SCRIPT>
|
Creating and Controlling the Family Tree
First we need to define all the JavaScript functions
that will be used in the Family Tree:
The first section is now pretty routine stuff, for all
those that have been reading previous articles. The utility functions, padout(),
y2k() and makeArray() are used by the HowOld()
function to calculate the 'age' between two dates. This is used later on to
calulate a persons age today, or their age when they died.
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript"><!--
function padout(number) {
return (number < 10) ? '0' + number : number;
}
function y2k(number) {
return (number < 1000) ? eval(number) + 1900 : number;
}
function makeArray() {
for (i = 0; i<makeArray.arguments.length; i++) this[i+1] =
makeArray.arguments[i];
}
var months = new makeArray('Jan','Feb','Mar','Apr','May','Jun',
'Jul','Aug','Sep','Oct','Nov','Dec');
var today = new Date();
var thisYear = y2k(today.getYear());
var thisMonth = today.getMonth()+1;
var thisDay = today.getDate();
function HowOld(day,month,year,thisDay,thisMonth,thisYear) {
var yearsold = thisYear - year, monthsold = 0, daysold = 0;
if (thisMonth >= month)
monthsold = thisMonth - month;
else {
yearsold--;
monthsold = thisMonth + 12 - month;
}
if (thisDay >= day)
daysold = thisDay - day;
else {
if (monthsold > 0)
monthsold--;
else {
yearsold--;
monthsold+=11;
}
daysold = thisDay + 31 - day;
}
if (yearsold < 0)
return '';
if ((yearsold == 0) && (monthsold == 0) && (daysold == 0))
return '';
return yearsold + ' years, ' + monthsold + ' months, '
+ daysold + ' days ';
}
|
The show() changes the contents of the two
frames, by reloading the frame contents.
function show(index) {
if (index != 0) {
PersonToShow = index;
window.topFrame.location.href = 'topframe.htm';
window.botFrame.location.href = 'botframe.htm';
}
}
|
The show() function is invoked when one of the
links formated by the showTree() function is selected (i.e. when the
user navigates around the Family Tree). If the show() function is
passed zero as the value of the index variable, then the documents will
not be updated. This is used to good effect in the following showTree()
function, where the current person is shown as a link with an onMouseover
event handler, but clicking the link does not perform anything.
The showTree() function formats the HTML to be
loaded into the topFrame which create the Family Tree structure
(Father, Mother, Spouse and Children). It uses various properties of the PersonArray[]
array using the passed index. It formats part of the output as links
which, as well as invoking the parent frames show() function
when the link is clicked, also have onMouseover event handlers, which
invoke the, sofar unused, updateDetails() function in the topFrame.htm
file. This replaces the contents on the botFrame with the details of
the person that the mouse is currently over. When the mouse moves away from the
link, then the original details of the currently selected person are reshown in
the botFrame.
function showTree(index) {
var a = '<TR><TD ALIGN=CENTER WIDTH=50%><FONT FACE=
"verdana,arial,helvetica">';
var b = '<\/TD><TD ALIGN=CENTER WIDTH=50%><FONT FACE=
"verdana,arial,helvetica">';
var c = '<\/TD><\/TR>';
var d = '<FONT FACE="verdana,arial,helvetica">_';
var fatherNo = PersonArray[index].fatherNo;
var motherNo = PersonArray[index].motherNo;
var spouseNo = PersonArray[index].spouseNo;
var output = '<CENTER><TABLE HEIGHT=100%><TR><TD>
<TABLE WIDTH="590">' +
a + '<B>Father:<\/B><BR>' +
'<A HREF="javascript:parent.show(' + fatherNo + ')" ' +
'onMouseover="updateDetails(' + fatherNo + ')">' +
PersonArray[index].father + '<\/A>' +
'<\/TD><TD>' + d + b + '<B>Mother:<\/B><BR>' +
'<A HREF="javascript:parent.show(' + motherNo + ')" ' +
'onMouseover="updateDetails(' + motherNo + ')">' +
PersonArray[index].mother + '<\/A>' + c +
a + '|' + '<TD><\/TD>' + b + c + a +
'<A HREF="javascript:parent.show(0)" ' +
'onMouseover="updateDetails(' + index + ')" ><B>' +
PersonArray[index].name + '<\/B><\/A>' +
'<\/TD><TD>' + d + b + '<B>Spouse:<\/B><BR>' +
'<A HREF="javascript:parent.show(' + spouseNo + ')" ' +
'onMouseover="updateDetails(' + spouseNo + ')">' +
PersonArray[index].spouse + '<\/A>' + c +
a + '|' + '<\/TD><TD>' + b + c +
a + '<B>Children<\/B>:<BR>';
for (var i=0; i<PersonArray[index].children; i++) {
var childNo = PersonArray[index]['child'+i+'No'];
output += '<A HREF="parent.show(' + childNo + ')" ' +
'onMouseover="updateDetails(' + childNo + ')">' +
PersonArray[index]['child'+i] + '<\/A><BR>';
}
output += '<\/TD><TD>' + b + c + '<\/TABLE><\/TD><\/TR>
<\/TABLE><\/CENTER>';
return output;
}
|
The two functions setPerson() and Person()
are used to create an instance of and define the Person object, which is
subsequently held in the PersonArray[] array.
The Person() function sets the properties of
the Person object that were passed to it, as well as calculating other
appropriate properties, birth date, death date, age.
Further details not yet know at the time the Person object is created
are left initialised.
The function also registers a method of the Person
object called setRelations(), when a Person objects setRelations()
method is used, the resolveRelations() function is invoked.
function setPerson(name,dobDay,dobMonth,dobYear,dobLocation,dodDay,
dodMonth,dodYear,dodLocation) {
return PersonArray[PersonArrayIndex++] = new Person(name,dobDay,
dobMonth,dobYear,dobLocation,dodDay,dodMonth,dodYear,dodLocation);
}
function Person(name,dobDay,dobMonth,dobYear,dobLocation,dodDay,
dodMonth,dodYear,dodLocation) {
this.No = PersonArrayIndex - 1;
this.name = name;
if (dobDay != 0 && dobMonth !=0 && dobYear !=0)
this.birth = padout(dobDay) + '-' + months[dobMonth] + '-'
+ y2k(dobYear) + ' <B>in<\/B> ' + dobLocation;
else
this.birth = 'unknown';
if (dodDay != 0 && dodMonth !=0 && dodYear !=0) {
this.death = padout(dodDay) + '-' + months[dodMonth] + '-'
+ y2k(dodYear) + ' <B>in<\/B> ' + dodLocation;
if (this.birth != 'unknown')
this.age = HowOld(dobDay,dobMonth,dobYear,dodDay,
dodMonth,dodYear);
else
this.age = 'unknown';
}
else {
this.death = 'N/A';
if (this.birth != 'unknown')
this.age = HowOld(dobDay,dobMonth,dobYear,thisDay,
thisMonth,thisYear);
else
this.age = 'unknown';
}
// The following details will all be resolved later:
this.father = '';
this.fatherNo = 0;
this.mother = '';
this.motherNo = 0;
this.spouse = '';
this.spouseNo = 0;
this.children = 0;
this.details = '<BODY BGCOLOR="papayawhip" TEXT="black">
<\/BODY>';
this.setRelations = resolveRelations;
}
|
The following resolveRelations() function will
update all those properties of the Person object that were originally
just initialised. The function is passed three or more other Person
objects which are used to update the properties. It expects to receive a father,
mother and spouse Person object as a minimum. It uses
the resolveRelations() functions arguments[] array to retrieve
any additional child person objects. For each child parent
object received it creates additional properties of the current Person
object.
The function also preformats HTML code which is placed
in the Person objects details property. This is used to later
overwrite the contents of the botFrame. Because the HTML code is formatted
and stored in the details property, it can be used to change the
contents of the botFrame frame instantaneously. There is no need to
wait for a document to be loaded.
function resolveRelations(father,mother,spouse) {
this.father = father.name;
this.fatherNo = father.No;
this.mother = mother.name;
this.motherNo = mother.No;
if (spouse) {
this.spouse = spouse.name;
this.spouseNo = spouse.No;
}
for (var i = 3; i < resolveRelations.arguments.length; i++,
this.children++) {
this['child' + this.children] = resolveRelations.arguments
[i].name;
this['child' + this.children + 'No'] = resolveRelations.arguments
[i].No;
}
var a = '<TR><TD VALIGN=TOP><FONT FACE="verdana,arial,helvetica"><B>';
var b = '<\/B><\/TD><TD WIDTH=100% VALIGN=TOP><FONT FACE="verdana,
arial,helvetica">';
var c = '<\/TD><\/TR>';
var details = '<CENTER><H1><FONT FACE="verdana,arial,helvetica">' +
this.name + '<\/FONT><\/H1><HR><TABLE WIDTH="590">' +
a + 'Date of Birth:' + b + this.birth + c +
a + 'Date of Death:' + b + this.death + c +
a + 'Age:' + b + this.age + c +
a + 'Spouse:' + b + this.spouse + c +
a + 'Children:' + b;
for (var i=0; i<this.children; i++)
details += this['child' + i] + '<BR>';
details += c + '<\/TABLE><\/CENTER><HR>';
this.details = '<BODY BGCOLOR="papayawhip" TEXT="black">' + details;
}
|
The Raw Data
The following data defines a small subset of the
British Royal Family Tree. It uses the setPerson() function to create Person
objects which are stored in the PersonArray[] array. The data for each
person consists of their name, date of birth, location of birth, date of death
and location of death of relevant. A reference to each object is also held in a
named variable, the variable names are chosen to represent the Person object.
var jan=1,feb=2,mar=3,apr=4,may=5,jun=6,jul=7,aug=8,sep=9,oct=10,nov=11,
dec=12;
var PersonArrayIndex = 0;
var PersonArray = new Array();
// Define an unknown person:
var anon = setPerson('','',0,0,0,'',0,0,0,'','','');
// Define everyone:
var gVI = setPerson('George VI WINDSOR, <I>King of England<\/I>',
14,dec,1895,
'York Cottage, Sandringham, Norfolk, England',6,feb,1952,'Sandringham,
Norfolk,
England');
var e_a_m = setPerson('<I>Lady<\/I> Elizabeth Angela Marguerite BOWES-LYON',
4,aug,
1900,'London, England',0,0,0,'');
var eII = setPerson('Elizabeth II Alexandra Mary WINDSOR,
<I>Queen of England<\/I>',
21,apr,1926,'17 Bruton St., London, W1, England',0,0,0,'');
var pp = setPerson('<I>Prince<\/I> Philip MOUNTBATTEN',10,jun,1921,
'Isle of Kerkira,
Mon Repos, Corfu, Greece',0,0,0,'');
var pc = setPerson('<I>Prince<\/I> Charles Philip Arthur WINDSOR',
14,nov,1948,
'Buckingham Palace, London, England',0,0,0,'');
var d_f_s = setPerson('<I>Lady<\/I> Diana Frances SPENCER',1,jul,1961,
'Park House,
Sandringham, Norfolk, England',31,aug,1997,'Paris, France');
var pw = setPerson('<I>Prince<\/I> William Arthur Philip WINDSOR',21,
jun,1982,
'St. Mary\'s Hosp., Paddington, London, England',0,0,0,'');
var ph = setPerson('<I>Prince<\/I> Henry Charles Albert WINDSOR',15,
sep,1984,
'St. Mary\'s Hosp., Paddington, London, England',0,0,0,'');
var psa = setPerson('<I>Princess<\/I> Anne Elizabeth Alice WINDSOR',
15,aug,1950,
'Clarence House, St. James, England',0,0,0,'');
var m_a_p = setPerson('<I>Captain<\/I> Mark Anthony Peter PHILLIPS',
22,sep,1948,'
',0,0,0,'');
var p_m_a = setPerson('Peter Mark Andrew PHILLIPS',15,nov,1977,
'St. Mary\'s Hosp.,
Paddington, London, England',0,0,0,'');
var z_a_e = setPerson('Zara Anne Elizabeth PHILLIPS',15,nov,1977,
'St. Mary\'s Hosp.,
Paddington, London, England',0,0,0,'');
var pa = setPerson('Andrew Albert Christian WINDSOR,
<I>Duke of York<\/I>',19,feb,
1960,'Belgian Suite, Buckingham Palace, England',0,0,0,'');
var s_m_f = setPerson('Sarah Margaret FERGUSON,
<I>Duchess of York<\/I>',15,oct,1959,
'',0,0,0,'');
var psb = setPerson('<I>Princess<\/I>
Beatrice Elizabeth Mary WINDSOR',8,aug,1988,
'Portland Hosp., England',0,0,0,'');
var pse = setPerson('<I>Princess<\/I>
Eugenie Victoria Helena WINDSOR',23,mar,1990,
'London, England',0,0,0,'');
var pe = setPerson('<I>Prince<\/I>
Edward Anthony Richard WINDSOR',10,mar,1964,
'Buckingham Palace, London, England',0,0,0,'');
var psm = setPerson('<I>Princess<\/I>
Margaret Rose WINDSOR',21,aug,1930,'Glamis Castle,
Angus, Scotland',0,0,0,'');
var a_c_r = setPerson('Anthony Charles Robert ARMSTRONG-JONES,
<I>Earl of Snowdon<\/I>',
7,mar,1930,'',0,0,0,'');
var d_a_r = setPerson('David Albert Charles ARMSTRONG-JONES,
<I>Vicount Linley<\/I>',3,
nov,1961,'',0,0,0,'');
var s_f_e = setPerson('<I>Lady<\/I>
Sarah Frances Elizabeth ARMSTRONG-JONES',1,may,1964,
'',0,0,0,'');
|
Once all the Person objects have been created
we can resolve all the relationships between them. Using the setRelations()
function as a function of each Person object in turn, we set the father,
mother, spouse and child properties by passing the
named variable reference the appropriate Person objects.
To place boundaries on the family tree, i.e. where we
are not interested in people or we don't know the details, we use the anon
Person object created above. The anon Person as empty
values for its properties, which allows us to use it in the family tree as a
normal relationship, but because the properties are blank the HTML links
formatted are empty.
// Resolve relationships:
gVI.setRelations(anon,anon,e_a_m,eII,psm);
e_a_m.setRelations(anon,anon,gVI,eII,psm);
eII.setRelations(gVI,e_a_m,pp,pc,psa,pa,pe);
pp.setRelations(anon,anon,eII,pc,psa,pa,pe);
pc.setRelations(pp,eII,d_f_s,pw,ph);
d_f_s.setRelations(anon,anon,pc,pw,ph);
pw.setRelations(pc,d_f_s,null);
ph.setRelations(pc,d_f_s,null);
psa.setRelations(pp,eII,m_a_p,p_m_a,z_a_e);
m_a_p.setRelations(anon,anon,psa,p_m_a,z_a_e);
p_m_a.setRelations(m_a_p,psa,null);
z_a_e.setRelations(m_a_p,psa,null);
pa.setRelations(pp,eII,s_m_f,psb,pse);
s_m_f.setRelations(anon,anon,pa,s_m_f,psb,pse);
psb.setRelations(pa,s_m_f,null);
pse.setRelations(pa,s_m_f,null);
pe.setRelations(pp,eII,null);
psm.setRelations(gVI,e_a_m,a_c_r,d_a_r,s_f_e);
a_c_r.setRelations(anon,anon,psm,d_a_r,s_f_e);
d_a_r.setRelations(a_c_r,psm,null);
s_f_e.setRelations(a_c_r,psm,null);
var details = PersonToShow = eII.No;
//--></SCRIPT>
|
The last line of the above script, sets the starting
point PersonToShow to the No property of eII, in this
case Elizabeth II Alexandra Mary WINDSOR, Queen of England. Each Person
object has a No property that represents its index entry within the PersonArray[]
array. It also sets the value of the details variable to this starting
point, which is used to determine whether the botFrame should be
replaced.
Finally all thats required is the frameset definition:
</HEAD>
<FRAMESET ROWS="50%,*" FRAMEBORDER=0 BORDER=0>
<FRAME SRC="topframe.htm" NAME="topFrame" SCROLLING=AUTO MARGINWIDTH=1
MARGINHEIGHT=1 NORESIZE>
<FRAME SRC="botframe.htm" NAME="botFrame" SCROLLING=AUTO MARGINWIDTH=1
MARGINHEIGHT=1 NORESIZE>
</FRAMESET>
|
Working Example
Try this example: British
Royal Family Tree.
NOTE: The above link will open in a new window.
Source Code
You can view the source code of the three files:
Articles | Family Trees |
back
|