Counting the "Children Count" of a parent element in a collapsible tree diagram using D3

I have been working on creating a collapsible tree chart using D3 js, and I have managed to meet some of the requirements. However, I am looking to add the functionality of displaying the number of children for each parent node.

Here is an example:

Click here to view the image

I'm having trouble figuring out the logic for this part, so any assistance would be greatly appreciated.

Thank you!

Below is my code without the numbering....

tree_d3.js

var treeData =
  {
    "name": "Top Level",
    "children": [
      { 
        "name": "Level 2: A",
        "children": [
          { "name": "Son of A" },
          { "name": "Daughter of A" }
        ]
      },
      { "name": "Level 2: B",
      "children": [
          { "name": "Son of B" },
          { "name": "Daughter of B" }
        ]
      },
    ]
  };

// Set the dimensions and margins of the diagram
var margin = {top: 20, right: 90, bottom: 30, left: 90},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select(".tree").append("svg")
    .attr("width", width + margin.right + margin.left)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate("
          + margin.left + "," + margin.top + ")");

var i = 0,
    duration = 750,
    root;

// declares a tree layout and assigns the size
var treemap = d3.tree().size([height, width]);

// Assigns parent, children, height, depth
root = d3.hierarchy(treeData, function(d) { return d.children; });
root.x0 = height / 2;
root.y0 = 0;

// Collapse after the second level
root.children.forEach(collapse);

update(root);

// Collapse the node and all it's children
function collapse(d) {
  if(d.children) {
    d._children = d.children
    d._children.forEach(collapse)
    d.children = null
  }
}

function update(source) {

  // Assigns the x and y position for the nodes
  var treeData = treemap(root);

  // Compute the new tree layout.
  var nodes = treeData.descendants(),
      links = treeData.descendants().slice(1);

  // Normalize for fixed-depth.
  nodes.forEach(function(d){ d.y = d.depth * 180});

...

tree_d3.css

.node circle {
  fill: #fff;
  stroke: steelblue;
  stroke-width: 3px;
}

.node text {
  font: 12px sans-serif;
}

.link {
  fill: none;
  stroke: #ccc;
  stroke-width: 2px;
}

tree_d3.html

<html>
<meta charset="UTF-8">
<body>
  <div class="tree">
  </div>
</body>
</html>

And here is the link to my code in CodePen...

CodePen Link

Answer №1

Providing a potential solution below.

  nodeEnter.append('text') 
    .attr("dy", ".35em")
    .attr("x", function(d) {
          return d.children || d._children ? 4 : -4;
     })
    .attr("text-anchor", function(d) {
      return d.children || d._children ? "end" : "start";
    })
    .text(function(d) {
      var children = d.children || d._children;
      return children ? children.length : 0;
    });

var treeData = {
  "name": "Top Level",
  "children": [{
      "name": "Level 2: A",
      "children": [{
          "name": "Son of A"
        },
        {
          "name": "Daughter of A"
        }
      ]
    },
    {
      "name": "Level 2: B",
      "children": [{
          "name": "Son of B"
        },
        {
          "name": "Daughter of B"
        }
      ]
    },
  ]
};

// Defining diagram dimensions and margins
var margin = {
    top: 20,
    right: 90,
    bottom: 30,
    left: 90
  },
  width = 960 - margin.left - margin.right,
  height = 500 - margin.top - margin.bottom;

// Appending SVG object to the page body
var svg = d3.select(".tree").append("svg")
  .attr("width", width + margin.right + margin.left)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", "translate(" +
    margin.left + "," + margin.top + ")");

var i = 0,
  duration = 750,
  root;

// Declaring and sizing the tree layout
var treemap = d3.tree().size([height, width]);

// Assigning parent, children, height, depth
root = d3.hierarchy(treeData, function(d) {
  return d.children;
});
root.x0 = height / 2;
root.y0 = 0;

// Collapsing after the second level
root.children.forEach(collapse);

update(root);

// Collapse the node and its children
function collapse(d) {
  if (d.children) {
    d._children = d.children
    d._children.forEach(collapse)
    d.children = null
  }
}

function update(source) {

  // Assigning x and y position for nodes
  var treeData = treemap(root);

  // Calculating new tree layout
  var nodes = treeData.descendants(),
    links = treeData.descendants().slice(1);

  // Normalizing for fixed-depth.
  nodes.forEach(function(d) {
    d.y = d.depth * 180
  });

  // Updating the nodes...
  var node = svg.selectAll('g.node')
    .data(nodes, function(d) {
      return d.id || (d.id = ++i);
    });

  // Enter any new modes at parent's previous position.
  var nodeEnter = node.enter().append('g')
    .attr('class', 'node')
    .attr("transform", function(d) {
      return "translate(" + source.y0 + "," + source.x0 + ")";
    })
    .on('click', click);

  // Adding Circle for the nodes
  nodeEnter.append('circle')
    .attr('class', 'node')
    .attr('r', 1e-6)
    .style("fill", function(d) {
      return d._children ? "lightsteelblue" : "#fff";
    });

  // Adding labels for the nodes
  // Continue updating text content here...

  // Transition to proper position for nodes
  // Continue transition process here...

}

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

How to maximize efficiency by utilizing a single function to handle multiple properties in Angular

I have 2 distinct variables: $scope.totalPendingDisplayed = 15; $scope.totalResolvedDisplayed = 15; Each of these variables is connected to different elements using ng-repeat (used for limitTo) When the "Load More" button is clicked (ng-click="loadMore( ...

Best practices for integrating text and images within a website header using CSS and HTML

I need help figuring out the most efficient method for creating a page header that showcases an image while keeping the text visible for search engines and in case the image fails to load. According to Google, it is recommended to use a <div> for th ...

Stop ckeditor from triggering set callback when the text is modified by an external source

Currently, I am developing a Vue project that involves the use of ckeditor5 as a text editor. Pusher, a real-time API utilizing websockets, is used to broadcast events to other Pusher instances in real time. Learn more about Pusher here. Within the ckedit ...

Jesting supplier, infusing elements

I find myself in a complex situation that I will do my best to explain, even if it may seem confusing. Imagine I have a customized provider called actionProvider within the module named core. This provider has the ability to register actions and then exec ...

if and elsif JSON with Angular

Currently, I am working on completing a task within our project. To provide more context, I have a field with items that I must select. Once I choose an item, another field appears in which I must select additional elements. These elements need to be sen ...

Can you explain the distinction between postMessage() and dispatchEvent() in relation to the origin policy?

Here is some code that I wrote. I tried setting the MessageEvent's origin to *, but I'm still getting an error in the console saying "Blocked a frame with origin "AAAA" from accessing a frame with origin "BBBB". Protocols, domains, and ports must ...

Is there a way to create a function in JavaScript that eliminates duplicate Objects within an Array of Objects?

Currently, I'm working on a function to store details of a couch in a JS object with 3 properties locally. The properties include: An ID (obtained from the product URL using a function) A color (retrieved through an event listener) A quantity ...

My router-outlet is malfunctioning when trying to display my component

I am currently diving into learning Angular 2 in order to revamp my personal website. However, I've encountered an issue where my application fails to load the component when I navigate to the appropriate route by clicking on the navigation bar. Insi ...

Encountering CORS problems when sending a request containing JSON data in the body

I have a local react application running on port 3000 and an express server running on port 8000. I am trying to make a request from within my react app as follows: fetch('http://localhost:8000/login', { method: 'POST', headers ...

Loop through an array of values in AngularJS

Here is an example of an array: [ {type:'x', value:'123'}, {type:'y', value:'456'}, {type:'z', value:'789'}, {type:'w', value:'012'}, {type:'q&apo ...

Ways to divide background color in half using CSS

Looking for some help with CSS - I want to split an HTML page into two sections, with a background and text/login form on top. Can anyone provide sample CSS and HTML code? This is what I've tried so far in CSS: .logindiv { height: 200px; width: ...

Attributes of the host element in Angular 1.5 components

I am seeking a way to customize attributes on the host element for Angular 1.5 components. What is the purpose? I would like to assign a class to a component in order to apply specific styles. For example, if the parent component has display: flex set, ...

The problem of "undefined function appendTo" is causing issues

My jQuery code looks like this: $(function() { var productTextTemplate = $('#product-text-template').html(); var productTemplate = $('product-template').html(); var product = productTextTemplate.appendTo(productTemplate); a ...

Is it possible to trigger a submit button to perform a POST request and an AJAX call simultaneously

I have a form that sends a POST request to show_aht2.php, but I also need it to trigger an AJAX call as shown in the code below. How can I achieve this without redirecting the user to another page? I want the user to remain on map.php. Thank you in advanc ...

Issue encountered when attempting to use array.sort on values exceeding 1000

When I use the .sort() method on my firstArr example, it functions correctly. However, when I apply the .sort() method to secondArr, which contains values exceeding 1000, it malfunctions. I have attempted to locate information on any limitations of the . ...

What is the best way to center all of my list items so that they are aligned correctly?

I have created a spec table for the Watch 4 using this code snippet: <div class="specs"> <ul class="info"> <li>Size</li> <li>44 mm</li> </ul> </div> < ...

Creating a "briefing" iframe at the top of a webpage

I'm looking for a way to allow users to embed my iframe at a specific size, like 100x100, and then have it resize dynamically to fit the page size when they click on it. I want this functionality to work regardless of how the HTML embedding the iframe ...

Form values not being transmitted correctly in Ajax POST request

I'm facing an issue with my form submission through AJAX where the values are coming out incorrect. Strangely, when I use a normal POST submit it works perfectly fine! <script type="text/javascript"> $(function () { $('#B ...

"Launching a node server in Azure to get you up and running

Currently, I have a Single Page Application that is hosted on Microsoft Azure. This application requires refreshing some dashboard data every 5 seconds. To achieve this, I have developed a nodejs service that continuously requests data from the API and gen ...

AngularJS promise fails to resolve when attempting to read a file using the FileReader

Can someone assist me with a function I am trying to create in my service? I want the function to use FileReader to read a small image file and return the result in a promise to my controller. The issue is that while the file reaches the service without an ...