Use CSS and JavaScript to create a visually appealing and interactive tree structure. Give the tree a dynamic design

I have been experimenting with CSS and JavaScript to create a dynamic graph based on a data table. You can view the graph that I need to replicate using CSS and JavaScript in this PDF. The example provided below showcases what I am attempting to achieve, although it is not dynamic.

/*CSS Code*/
* {margin: 0; padding: 0;}

.tree ul {
padding-top: 20px; position: relative;

transition: all 0.5s;
-webkit-transition: all 0.5s;
-moz-transition: all 0.5s;
}

.tree li {
float: left; 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;
}

/*Using ::before and ::after for 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;
}

/*Removing connectors from elements without siblings*/
.tree li:only-child::after, .tree li:only-child::before {
display: none;
}

/*Adjusting spacing for single children*/
.tree li:only-child{ padding-top: 0;}

/*Removing connectors from first and last child*/
.tree li:first-child::before, .tree li:last-child::after{
border: 0 none;
}
/*Adding vertical connector to 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;
}

/*Vertically connecting parents*/
.tree ul ul::before{
content: '';
position: absolute; top: 0; left: 50%;
border-left: 1px solid #ccc;
width: 0; height: 20px;
}

.tree li a{
border: 1px solid #ccc;
padding: 5px 10px;
text-decoration: none;
color: #666;
font-family: arial, verdana, tahoma;
font-size: 11px;
display: inline-block;

border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;

transition: all 0.5s;
-webkit-transition: all 0.5s;
-moz-transition: all 0.5s;
}

/*Hover effects*/
.tree li a:hover, .tree li a:hover+ul li a {
background: #c8e4f8; color: #000; border: 1px solid #94a0b4;
}
.tree li a:hover+ul li::after, 
.tree li a:hover+ul li::before, 
.tree li a:hover+ul::before, 
.tree li a:hover+ul ul::before{
border-color:  #94a0b4;
}

   
<div class="tree">
    <ul>
    <li>
    <a href="#">level_0</a>
    <ul>
    <li>
    <a href="#">service_0</a>
    <ul>
    <li>
    <a href="#">output_0</a>
    </li>
    </ul>
    </li>
    <li>
    <a href="#">service_1</a>
    <ul>
    <li><a href="#">output_0</a></li>
    <li>
    <a href="#">output_1</a>
    <ul>
    <li>
    <a href="#">input_0</a>
    </li>
    <li>
    <a href="#">input_1</a>
    </li>
    <li>
    <a href="#">input_2</a>
    </li>
    </ul>
    </li>
    <li><a href="#">output_2</a></li>
    </ul>
    </li>
    </ul>
    </li>
    </ul>
    </div>

Answer №1

Your beautiful CSS tree inspired me to create a tree builder, but constructing it requires a predefined object. Representing your PDF accurately hinges on establishing the number of elements and levels beforehand.

$(function() {
  var tree = {
    label: "Level 0",
    child: [{
      label: "Service 0",
      child: [{
        label: "Output 0",
        child: []
      }]
    }, {
      label: "Service 1",
      child: [{
        label: "Output 0",
        child: []
      }, {
        label: "Output 1",
        child: [{
          label: "input 0",
          child: []
        }, {
          label: "Input 1",
          child: []
        }, {
          label: "Input 2",
          child: []
        }]
      }, {
        label: "Output 2",
        child: []
      }]
    }]
  }

  function buildTree(tree) {
    var html = ""
    var label = "<a href='#'>" + tree.label + "</a>"
    var child = tree.child
    html += "<li>" + label
    if (tree.child.length > 0) {
      html += "<ul>";
      $.each(tree.child, function(i, elem) {
        html += buildTree(elem);
      });
      html += "</ul>";
    } else {
      html += "</li>";
    }
    return html
  }
  $(".tree").append("<ul>"+buildTree(tree)+"</ul>")
})
/*Now the CSS*/

* {
  margin: 0;
  padding: 0;
}
.tree ul {
  padding-top: 20px;
  position: relative;
  transition: all 0.5s;
  -webkit-transition: all 0.5s;
  -moz-transition: all 0.5s;
}
.tree li {
  float: left;
  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 a {
  border: 1px solid #ccc;
  padding: 5px 10px;
  text-decoration: none;
  color: #666;
  font-family: arial, verdana, tahoma;
  font-size: 11px;
  display: inline-block;
  border-radius: 5px;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  transition: all 0.5s;
  -webkit-transition: all 0.5s;
  -moz-transition: all 0.5s;
}
/*Time for some hover effects*/

/*We will apply the hover effect the lineage of the element also*/

.tree li a:hover,
.tree li a:hover+ul li a {
  background: #c8e4f8;
  color: #000;
  border: 1px solid #94a0b4;
}
/*Connector styles on hover*/

.tree li a:hover+ul li::after,
.tree li a:hover+ul li::before,
.tree li a:hover+ul::before,
.tree li a:hover+ul ul::before {
  border-color: #94a0b4;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="tree">
</div>

Answer №2

After some troubleshooting, I managed to find a workaround for my issue. It may not be a universal fix, but it worked for me.

Here is the snippet of EJS code that helped:

<div class="tree">
    <ul>
        <li>
            <a href="#">Niveau <%=level_selected%></a>
            <ul>
                <%var type_service = null;
                    data.forEach(function(docService) {
                    if(docService.type_service != type_service){%>

                <li>
                    <a href="#"><%= docService.service%></a>
                    <ul>
                        <% var class_output = null;
                            data.forEach(function(docOutput) {
                            if(docOutput.type_service == docService.type_service){
                                if(docOutput.class_output != class_output){%>

                            <li>
                                <a href="#"><%= docOutput.output%></a>
                                <ul>
                                    <%
                                        data.forEach(function(docInput) {
                                        if(docInput.type_service == docService.type_service){
                                            if((docInput.class_output == docOutput.class_output)&&(docInput.input != null)){ %>
                                            <li>
                                                <a href="#"><%=docInput.input%></a>
                                            </li>
                                            <%}}});%>
                                </ul>
                            </li>
                            <%}class_output=docOutput.class_output;
                        }});%>

                    </ul>
                </li>
                <% }type_service=docService.type_service;});%>
            </ul>
        </li>
    </ul>
</div>

I share this solution in the hope that it can benefit others facing a similar challenge.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Ways to retrieve the chosen option in a dropdown list without specifying the dropdown's name, id,

Custom dropdown, Model-View-Controller Code @foreach (var attribute in Model) { string controlId = string.Format("product_attribute_{0}_{1}_{2}", attribute.ProductId, attribute.ProductAttributeId, attribute.Id); @switch (attribute.AttributeControl ...

JavaScript not running following an AJAX request

I’ve been sifting through numerous posts on SO, but haven’t found a solution yet. I'm currently modifying a Wordpress blog where I need the prev/next buttons on a single post page to dynamically load the adjacent posts via Ajax. I believe I have ...

execute a different function once the animation has finished running with the .bind

When attempting to detect the completion of a CSS animation in order to remove a class, I encountered an issue where using .bind causes the animation end event to trigger even for the other function. You can view the complete code here. How can I ensure ...

Remove HTML formatting from excel cells and fill separate cells

Is there a way to condense HTML code and fill different columns in Excel? For instance, If my HTML code looks like this: <p></p>10-16-2013 22:35<br/>I love pizza! Ordering was a breeze!<p></p>10-16-2013 13:19:46<br />t ...

Develop a design utilizing a foundational database entity

I'm new to AngularJS and I am seeking guidance on how to properly separate the model from the controller. In my previous experience, I have always integrated models within the controllers. For example: angular.module("app").controller("customerContr ...

The length of JSONPath in Javascript is significantly longer, approximately 3000 times lengthier than a traditional loop

I am experiencing performance issues with JSONPath implemented in JavaScript using the Stephan Goessner Library. Below is an example of the JSON structure causing the problem: [ { id:1, name: "lorem", elements: [ ...

Utilize Google Home to browse through nearby websites and respond to inquiries by incorporating Javascript

Can Google Home be programmed to read from a local webpage and provide answers based on the content of that page through Javascript? ...

Conceal navigation options on smaller screens and enable drop-down menu functionality with Bootstrap 3

With the usage of Bootstrap 3, I have successfully hidden two menu items when the screen is resized. However, these items still appear in the drop down menu that appears on the top left (with three bars) when the screen size is reduced. Is there a way to k ...

What is the best way to deal with empty arrays during serialization of modified form elements only?

Managing a form with numerous multi-select fields can be challenging, especially when dealing with paged ajax. With each select having a unique ID and named accordingly as values[foobar1][], values[foobar2][], values[foobar3][], and so on, it's crucia ...

Exploring reviews with an innovative combination of Node.js and MongoDB

Recently diving into the world of Nodejs and mongoDB, I am excited to create two distinctive pages: one for users to update their account details such as profile picture, email, and name - all of which would be saved into the database; and another for re ...

The entire DOM in Angular2+ flickers upon loading a component using ngFor

I am facing an issue where, after a user clicks on an item to add it to a list and then renders the list using ngFor, there is a flickering effect on the screen/DOM. The strange thing is that this flicker only happens after adding the first item; subsequen ...

The method _react.default.useContext does not exist as a function

I am currently working with Material UI Stepper. After using it, I noticed that the functionality is not working properly as expected from their website. To troubleshoot, I added a console.log inside the VerticalLinearStepper method. Upon checking the cons ...

Modifying button styles in Angular UI Datepicker

In this plunk, there is an Angular UI Datepicker with a template. I'm trying to customize the colors of the "Today", "Clear", and "Close" buttons by editing the popup.html. However, even after changing the classes in the code, the datepicker still sho ...

Is there a way to retrieve student data by searching for a specific field?

I have a search button on the front end that allows users to input a student number and retrieve the corresponding information from my schema... Currently, I am mapping the data for all products However, when attempting to log the data, I am getting a nu ...

What is the best way to activate multiple events within an overlapping area containing multiple divs at the same

How can I trigger events on same level divs that overlap in different areas? When there are multiple divs overlapping, only one of them gets triggered. Is there a way to trigger events on all overlapped same level divs? Here is an example code snippet: ...

Troubleshooting jQuery window scrolling and detecting element positions issue

I am utilizing the $(window).scroll(function() method to dynamically change classes on the navigation menu as different sections come into view on a website. Whenever a section becomes visible, the navigation class is updated to 'current'. $(win ...

Having Trouble Loading Vue Devtools in Vue Electron Builder

I'm encountering an issue with loading Vue Devtools in my Electron application integrated with Vue. This is my first time working with this combination, and I suspect there might be a dependency problem causing the Devtools not to load within the Elec ...

automatically changing radio button selection based on dropdown choice

Let's address the issue at hand: I have a dropdown list with over three options, and alongside it, a radio group for yes or no responses. What I am aiming to achieve is setting the radio button to "no" when option 1 is selected, and to "yes" when any ...

The synchronization between Typescript and the HTML view breaks down

I am currently working on an application that retrieves user event posts from MongoDB and displays them in HTML. In the Event-post.ts file, inside the ngOnInit() function, I have written code to retrieve the posts using the postsService.getPosts() method. ...

State is causing a conflict by allowing multiple menus to open when mapping over Menu objects

I am currently encountering an issue with my Material UI <Menu>. The problem arises when I attempt to display some data and interface functionality by mapping over a <Card>, along with adding an <IconButton> on each card that opens a menu ...