Search code examples
javascriptd3.js

d3.js incorrect visualization


I have this simple example I'm using to study d3.js. My goal is to plot a tree starting from a json data structure. I found that d3.js library is very useful to do this job, so I tried to create a minimum example to understand the basics. As you can see from the attached image, the links are plotted, but not all the nodes are not plotted. The last one, Pippo, is not plotted. What's missing?

Can anyone help me?

Thank you very much!

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Collapsible Tree with D3.js</title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
  <style>
    .node {
      cursor: pointer;
    }

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

    .node text {
      font: 12px sans-serif;
    }
    .link {
      fill: none;
      stroke: #ccc;
      stroke-width: 2px;
    }
  </style>
</head>
<body>

<script>
  // Sample JSON data
  const treeData = {
    "name": "Root",
    "children": [
      {
        "name": "Child 1",
        "children": [
          {
            "name": "Child 1.1",
            "children": [
              {
                "name":"Pippo"
              }
            ]
          },
          { "name": "Child 1.2" }
        ]
      },
      {
        "name": "Child 2",
        "children": [
          { "name": "Child 2.1" },
          { "name": "Child 2.2" }
        ]
      }
    ]
  };

  // ************** Generate the tree diagram  *****************
  var margin = {top: 20, right: 120, bottom: 20, left: 120},
  width = 960 - margin.right - margin.left,
  height = 500 - margin.top - margin.bottom;
  
  var i = 0;

  // Create SVG container
  const svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g")
    .attr("transform", "translate(50, 20)");

  // Create hierarchical data structure
  const root = d3.hierarchy(treeData);
  var tree = d3.tree().size([height, width]);
  tree(root);

  // Create links
  const link = svg.selectAll(".link")
    .data(root.links())
    .enter().append("path")
    .attr("class", "link")
    .attr("d", d3.linkHorizontal()
      .x(d => d.y)
      .y(d => d.x)
    );

  // Create nodes
  const node = svg.selectAll(".node")
    .data(root.descendants())
    .enter().append("g")
    .attr("class", "node")
    .attr("transform", d => `translate(${d.y},${d.x})`);

  // Debug print of structure
  console.log("Nodes are: "); 
  console.log(node); 

  // Add circles to nodes
  node.append("circle")
    .attr("r", 10);

  // Add text to nodes
  node.append("text")
    .attr("y", -20)
    .attr("x", -16)
    .attr("text-anchor", d => ("start"))
    .text(d => d.data.name);

</script>

</body>
</html>

Here is the result:

Visualization


Solution

  • Follow the standard margin convention in D3.js. d3.js margin convention

    
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Collapsible Tree with D3.js</title>
      <script src="https://d3js.org/d3.v7.min.js"></script>
      <style>
        .node {
          cursor: pointer;
        }
    
        .node circle {
          fill: #fff;
          stroke: steelblue;
          stroke-width: 3px;
        }
    
        .node text {
          font: 12px sans-serif;
        }
        .link {
          fill: none;
          stroke: #ccc;
          stroke-width: 2px;
        }
      </style>
    </head>
    <body>
    
    <script>
      // Sample JSON data
      const treeData = {
        "name": "Root",
        "children": [
          {
            "name": "Child 1",
            "children": [
              {
                "name": "Child 1.1",
                "children": [
                  {
                    "name":"Pippo"
                  }
                ]
              },
              { "name": "Child 1.2" }
            ]
          },
          {
            "name": "Child 2",
            "children": [
              { "name": "Child 2.1" },
              { "name": "Child 2.2" }
            ]
          }
        ]
      };
    
      // ************** Generate the tree diagram  *****************
      var margin = {top: 20, right: 50, bottom: 20, left: 50},
      width = 960 - margin.right - margin.left,
      height = 500 - margin.top - margin.bottom;
      
      var i = 0;
    
      // Create SVG container
      const svg = d3.select("body").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})`);
    
      // Create hierarchical data structure
      const root = d3.hierarchy(treeData);
      var tree = d3.tree().size([height, width]);
      tree(root);
    
      // Create links
      const link = svg.selectAll(".link")
        .data(root.links())
        .enter().append("path")
        .attr("class", "link")
        .attr("d", d3.linkHorizontal()
          .x(d => d.y)
          .y(d => d.x)
        );
    
      // Create nodes
      const node = svg.selectAll(".node")
        .data(root.descendants())
        .enter().append("g")
        .attr("class", "node")
        .attr("transform", d => `translate(${d.y},${d.x})`);
    
      // Debug print of structure
      console.log("Nodes are: "); 
      console.log(node); 
    
      // Add circles to nodes
      node.append("circle")
        .attr("r", 10);
    
      // Add text to nodes
      node.append("text")
        .attr("y", -20)
        .attr("x", -16)
        .attr("text-anchor", d => ("start"))
        .text(d => d.data.name);
    
    </script>
    
    </body>
    </html>