DYNAMIC TREE VIEW FOR NESTED LIST WITH CSS3
Dynamically Building Nested List from JSON data and Tree view with CSS3
A tutorial to dynamically build nested list from JSON data and vertical tree view with CSS3 only
Building a tree view for the hierarchical data is a common task in the front-end development of web applications. In most cases, hierarchical data is available in JavaScript Object Notation (JSON) which is a lightweight data format, and we need to represent this data as a tree on the webpage. In this tutorial, we will walk through the steps to dynamically create a nested list from the JSON data and style it to a vertically oriented tree view. The tutorial will use jQuery expression evaluation statement, Flexbox layout, and Pseudo-classes and Pseudo-elements from CSS3.
The goal of the tutorial can be divided into two parts:
- In the first part, we will learn how to dynamically build a nested unordered list of items from JSON data.
- Then we will plot a vertically oriented tree view from the nested list with pure CSS3.
The complete project code can be found here.
1. Building Nested List from JSON data
We will start with a simple data structure and then gradually move to the complex data structure. Let's consider the following simple data structure with a Parent
and one Child
:
const data = {
Parent: 'Child'
}
The HTML
markup to create UI
will look like the below snippet. For the sake of styling, we will use span
elements nested inside each li
.
<ul>
<li>Parent
<ul>
<li> <span> Child </span> </li>
</ul>
</li>
</ul>
Let’s write some JavaScript code to create HTML
the markup for the above data. We assume that there could be three data types in the data:
- Object
- String
- Array
In order to evaluate the above expressions, we will be using a switch statement with jQuery. We will use $.type
to evaluate three cases, a mockup of the function is:
We create an let markupArray = ['<ul>']
where we will push the HTML
tags dynamically. The process will start by calling thecreateList
function. As we are dealing with the JSON
data, we fall into the first case that is the object
. So in this case, we will write another function getItems
to iterate over the items of the object
. As we know that there in our data object, there is only one item Parent:"Child”
, so the function will return only one item Parent
. The function will create a li
parent item with nestedul
for its children.
- create
li
for theParent
with an openingul
tag for the children. - call the
createList
function for the value ofParent
(Child
in this case). - as
Parent
is astring
, we will fall under thestring
expression ofcreateList
the function that will just push theli
tags forChild
to themarkupArray
. - at the end of the iteration, we will push the closing tags.
Our modified createList
function will become:
Next, we will create a main function to call the createList
function and add a closing </ul>
tag to the markupArray
. Inside the main
function, we will make use of the regular Array.join function which will convert an array to a string. Let’s create a div element <div id="list"></div>
and set the contents to the concatenated string using the jQuery html method.
Summarily, we passed the data to the createList
expression evaluation function that will call the getItems
function as the data is of type object
. The getItems
function will iterate over the items of data and create a li
for parent item and a nested ul
list for the children. We will then pass each child to the evaluation function to check the type of children. At this point, the child is a string
so we just add a li
to the children ul
.
The source code up to this point can be found here, clone the repository and open the index.html
file in any of your favorite browsers.
Let’s consider the case where the parent element has more than one child, an array of children. In that case, we will update the array
case from createList
function as follows:
In the case of an array, we will iterate over the items of the array to further check their types. So the items will under one of the cases we have already defined i.e object, string, or array, and the function will execute the respective action according to the type. Let’s modify our data a bit and see this in action. If you have downloaded the project, then modify the data
variable in main.js
file and open the index.html
file in the browser.
1.1 Creating Family Tree
At this point, we have successfully created the tree view from the nested JSON data. However, according to the original idea of the project, we want to plot a family tree structure where each member of the family has some bunch of details and a picture as well. The end result of this part will look like this:
To adopt the structure as shown in the picture above, let’s clean up stuff and start from the beginning. Again, starting with the expression evaluation function createList
, as our data is object
, we will pass it to the getItems
function.
If we look at our new data structure, we see that we have two objects: one data
object and inside that there is aParent
object. The process will go through in following steps:
- For the first,
data
object, we want to push it as ali
item of rootul
.
- For the
Parent
object, we will iterate over the details ofParent
object. Let's create anothergetDetails
function. Inside this function, we will loop over the object items, fetch the item value and push aspan
tag for each item.
We will fetch details of the Parent
object and call the getDetails
function inside the iteration of getItems
function. It's important to note that here (details[detail]
) we are trying to get the values of the items. In this way, first, we find the object in our data and iterate over the object to get its details. The modified getItems
function:
The code up to this point can be found with this GitHub commit.
Before moving on, let’s tweak one change for the image. Right now, we just the image name but we want to place an iamge there. For this, we will place a if - else
statement to check if the detail
is an img
and put the img
tag instead of span
element. Also, in the data
object, we can move imge
item to the top of other items, and set style for span
and img
element as display:block
.
You can compare the GitHub commit for the code up till now.
Next, we have to deal with the children and for this let's add an array of children to the Parent
element. It's important that the child should adopt the same structure as the parent i.e. some details and an array of children.
Now we have three cases to evaluate: if the key of the item inside the object is img
, children
or something else. We already have approaches to deal with the img
and other stuff, so now we will proceed with the children
array. In essential, we need to create an ul
that will contain all the items in the array as li
. In this particular case:
- first, we will push a
<ul>
tag as this will be a nested list inside the parentli
. - Then, loop through the items of the array and pass each item to the
getItems
function. This function will push a<li>
tag and then callgetDetails
function (as items inside the array are objects) for each item. Thus, repeating the same process we did for the parent element. - At the end of the loop, we push the closing tag
</ul>
to themarkupArray
.
With this, we are done with the first part of the tutorial. You can find the complete code to this point here.
2. Plotting Vertical Tree View
The process of plotting vertical tree view can be broadly divided into the following steps: Flip the horizontal view to vertical view Draw connectors between the elements
2.1 Flip List to Vertical View
This is a fairly easy part and can be easily achieved using CSS Flexbox layout. In this case, we will set:
ul
elements to the vertical directionli
elements to the horizontal direction
The images are quite big, so the image width and height are set to 50px.
From the data structure, we know that ul
is the parent element and li
is the child element. To use the flexbox layout, we need to make the parent element a flex container. The elements inside the flex container will become the flex items. By default, the li
are block elements and are vertically stacked. Flexbox layout will align the flex items in a row in case direction is not specified. It is important to note that flexbox only applies to the items of the flex container, not to the nested items of flex items. However, for the current case, the nested items are again ul
so flexbox layout will be applied to them as well. As a result, we will have rows for all the ul
and nested ul
as well. Additionally, we will remove the default indentation and list-style as well.
As a result, we will see a vertical view of ul
with the li
in the horizontal direction (image 2 in the following screenshot). To change the direction of li
, we will apply the flexbox model to it and set the flexbox direction as column
(image 3). With these two simple styles, we have flipped the view to the horizontal tree view. This Github commit reflects the code till this point.
2.2 Draw Connectors
In this section, we will draw connectors:
- between the parent and siblings
- between the siblings and [sub]siblings
To achieve this, we will make use of Pseudo-classes and pseudo-elements. To place the connectors horizontally in the middle and vertically on the top of the element, we will use left:50%
and top:0
position specifiers respectively.
- Let’s start with adding vertical connectors before each parent element(
ul
), except the very first parent element. This will be done using the::before
pseudo-class relative to theul
, along with adding apadding-top
to theul
equal to the height of the pseudo-element. To hide the connector before the very first element oful
, we will use pseudo-class:first-child
to set the display of pseudo-element::before
tonone
. The result of this can be seen in image 1 of the below screenshot. - Adding connectors between the siblings will be done in two steps. First, we will add vertical connectors before each
li
element using::before
pseudo-element relative to the parentli
. In the next step, we will place horizontal connectors for the siblings using the::after
pseudo-element withwidth
andborder-top
properties. The resultant image is shown as image 2 in the screenshot below.
It can be seen from the above screenshot that we need to fix some issues:
- First, remove the horizontal connector from the very first element. This issue can be fixed with the use of
only-child
pseudo-class to hide the display of pseudo-elements in case there is only one child. - Next, remove the left and right connectors for the first and last element of a group respectively. This will be fixed using
first-of-type
andlast-of-type
pseudo-classes with a width of50%
and position right and left 0 respectively. - Finally, remove the
::before
connects for the single element in the group usingpadding-top:0
anddisplay:none
.
This GitHub commit shows the code up to this point.
2.4 Formatting Elements
To add some styles, we will wrap details span
elements into a single div
element. The following git diff screenshot shows what has been changed.
We will add some padding between the children, background color, and text transformation. The updated CSS code can be found here.
Happy coding🎉🎉🎉.
Resources:
Images used in this tutorial are downloaded from Flaticon.com