Exploring D3 to extract nested information and build an interactive navigation tree

Recently, I've been working with d3's zoom circle feature from here:

The data I'm dealing with is structured as a json array with various levels of arrays and objects.

object = {
    class: "show",
    name: "Game of Thrones",
    children : [
        {
            class: "family",
            name: "Starks",
            children: [
                {
                    class: "members",
                    name: "Rob"
                },
                {
                    class: "members",
                    name: "Jon Snow"
                }
            ],
        },
        {
            class: "family",
            name: "Lannisters",
            children: [
                {
                    class: "members",
                    name: "Ser Jaime"
                },
                {
                    class: "members",
                    name: "Cersei"
                }
            ],
        }            
    ],
}

Displaying the circles was not an issue, but now I'm attempting to create a navigation display on the side that outlines the hierarchical structure of the data. Essentially, I want something like this:

<ul>
  <li> Game of Thrones
    <ul>
      <li> Starks
        <ul>
          <li> Rob </li>
          <li> Jon Snow </li>
        </ul>
      </li>
      <li> Lannisters
        <ul> 
          <li> Ser Jaime </li>
          <li> Cersei </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

I've only managed to access one level down in the data so far, using 'divs' to build the structure step by step.

var sentimentData = data.children;

var navBox = d3.select("body").append("body:div");

navBox
    .attr("class", "navBox");

var sentimentNav = navBox.selectAll('div')
    .data(sentimentData)
    .enter()
    .append('div')
    .text(function(d){ return d.name; });

However, I've hit a roadblock when trying to proceed further down the hierarchy. My attempt at a recursive function below resulted in appending divs to the top node instead of their respective parent nodes.

function buildNav(d) {
 if (d.children) {
   children = d.children;
   d3.select('body').append('div')
      .data(children)
      .enter()
      .append('div')
      .attr("class", function(d) {return d.name; });
   children.forEach(buildNav); 
   d._children = d.children;  
   d.children = null;
 }
}

buildNav(data);

If you have any suggestions on how to properly append children to parents or access data multiple levels down, I would greatly appreciate it.

Answer №1

Hello there! Below is a complete example code that generates lists as per your request. It may not be the most elegant solution, but it achieves exactly what you mentioned. Feel free to enhance and customize this code for your specific needs!

Here's how the files are structured:

<!DOCTYPE html>
<html>
<head>
    <title>Navigation</title>
    <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script src="script.js"></script>
</head>
<body onload="load()">
    <h2>Navigation</h2>
    <h4>(using D3.js-generated navigation items)</h4>
</body>
</html>

Script file (script.js):

function load(){
    object = {
        class: "show", name: "Game of Thrones",
        children : [
            {
                class: "family", name: "Starks",
                children: [
                    { class: "members", name: "Rob" },
                    { class: "members", name: "Jon Snow" }
                ],
            },
            {
                class: "family", name: "Lannisters",
                children: [
                    { class: "members", name: "Ser Jaime"},
                    { class: "members", name: "Cersei" }
                ],
            }            
        ],
    };

    buildNav(null, object);

    function buildNav(parent_ul, node){
        var current_ul, current_li;
        if(parent_ul == null)
            parent_ul = d3.select("body").append("ul");
        current_li = parent_ul.append("li").text(node.name);
        if (node.children) {
            current_ul = current_li.append("ul");  
            for (var i=0; i<node.children.length; i++) {
                buildNav(current_ul, node.children[i]); 
            };
        };
    };
};

This code will generate a page with the desired navigation structure.

Feel free to reach out if you need further assistance or have additional questions!

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

Stop the annoying page flicker in Next.js 12 by implementing a custom dark mode using Tailwind CSS classes

Is there a way to prevent the page flash when implementing dark mode in Tailwind CSS using classes in Next.js v12 without relying on third-party packages like next-themes? I have explored different solutions: This StackOverflow Q&A How to fix dark mo ...

Change an array of JSON objects into a single string representation

Currently, I am working with jquery and have come across a JSON object array that needs to be converted into a specific string format. After some research, I found out about the "JSON.stringify" method but I am unsure of how to implement it in my scenario. ...

Tips on how to retrieve the value of a number-type input within a custom attribute directive

I have successfully implemented this code in IE 11, but now we are looking to transition away from IE11 and address some of the issues that have arisen. <input type="number" evaluate-input="model.personalNumber" ng-model="model ...

Connecting Ember controllers with views/elements

As a developer with an Angular background, I am relatively new to Ember. Let's consider a scenario where there are multiple elements, each containing different sets of data. #elem1 10 #elem2 20 #elem3 30 My objective is to link each of these indiv ...

Is it not odd that there are two '=>' symbols in an arrow function when there is typically only one? What could this possibly signify?

function updateStateValue(name) { return (event) => { setState({ ...state, [name]: event.target.checked }); }; } To view the full code example, visit CodeSandbox. ...

Oops! We encountered an issue trying to find the "list" view in the views directory

Here are the images I have: This is the code for my app.js file And this is the code for list.ejs located in the views directory Unfortunately, my index.html file is empty. Below is the full error log: Error: Failed to lookup view "list" in the views di ...

The alignment issue persists in HTML/CSS despite troubleshooting efforts

I am facing a challenge while attempting to center text within a modal window, despite my efforts the text remains uncentered. This is my HTML code: <div ng-init="modalCompassDir()"> <div class="myModal"> <img class='floor ...

jquery line break in textarea

There is a 'edit description' button that, when clicked, makes text disappear and reappear if (id == 'commedit') jQuery(this).html('<textarea>'+jQuery(this).text()+'</textarea>'); else i ...

Incorporate fresh Google sites into different web pages using iFrame integration

Wishing you a fantastic day! I am currently working on embedding a brand new Google site into another webpage. I attempted to use an iframe for this purpose, but unfortunately it did not work as expected. Here is the code snippet: <iframe width="1280 ...

Creating an API using PHP and JSON format

Recently, I decided to delve into JSON API to gain a better understanding of how it functions. Here is a JavaScript example that demonstrates the basics of working with JSON: $(document).ready(function() { var url = 'https://api.themoviedb.org/3/ ...

Exploring the wonders of ExpressJS session variables

Having transitioned from a PHP background to focusing on JS, I find myself adjusting to the differences in handling session variables. In PHP, the $_SESSION global variable was very convenient as it allowed easy access to session data throughout the code. ...

Is there a better way to implement an inArray function in JavaScript than using join and match?

Recently, I created an inArray function for JavaScript which seems to be working fine. It's short and a bit unusual, but I have a nagging feeling that there might be something wrong with it, although I can't quite pinpoint what it is: Array.prot ...

Password Field Validation in React

<TextField id="outlined-basic" label="Password" variant="outlined" /> Can anyone assist me in implementing password validation using an onchange function? I am seeking help with the ...

Developing a collection of reusable components in a Javascript bundle for enhanced efficiency

I currently have a backend rendered page (using Django) that I want to enhance by incorporating components from PrimeVue and a markdown editor wrapped as a Vue component. Previously, we utilized some simple animations with jQuery which we included directly ...

Sort Messages By Date Created

While using Vue, I encountered a general JavaScript question. I am fetching cat messages from an API for a chat interface. The initial call returns an array of objects where each object represents a chat message: data: [ {id: 1, created_at: "2022-05 ...

Error encountered while attempting to convert a unique custom object to JSON in Jersey

Before jumping to conclusions and marking this question as a duplicate, I want to clarify that I have already explored the solutions provided in A message body writer for Java class not found javax.ws.rs.WebApplicationException: com.sun.jersey.api.Messag ...

The v-for loop seems to only update the last element instead of all of them, which is incorrect

How can I ensure that all 3 page links in the nav are displayed in 3 languages of user choice? Initially, the language change works fine on page load. However, after clicking on a language change link once, only the last link's language changes instea ...

How about a custom-designed talking JavaScript pop-up?

Looking to customize the appearance (CSS, buttons...) of a "confirm" JavaScript dialog box while ensuring it remains persistent. The dialog box appears after a countdown, so it is crucial that it maintains focus even if the user is on another browser tab o ...

Retrieve the file that has been uploaded to AWS S3

Here is a snippet of code to consider : var express = require('express'), aws = require('aws-sdk'), bodyParser = require('body-parser'), multer = require('multer'), multerS3 = req ...

Using AJAX to upload jQuery file - sharing PHP variables

After setting up the blueimp jQuery file upload, I've encountered a problem that's stumping me. My goal is to retrieve a PHP variable that's posted when the file is uploaded and execute some PHP code if it exists. I have made modifications ...