0

I have been trying to reproduce this ObservableHQ code on horizontal collapsible tree (the second example) into regular html-css-js format. Below is my implementation.

const width = 4000;
let tree = data => d3.tree()
    .size([width * 2, width])
    .separation((a, b) => (a.parent == b.parent ? 1 : 3))
    (d3.hierarchy(data));
const data = d3.json("https://cdn.jsdelivr.net/gh/d3/d3-hierarchy@master/test/data/flare.json");
const svg = d3.select("svg")
    .style("width", "100%")
    .style("height", "auto")
    .style("padding", "10px")
    .style("box-sizing", "border-box")
    .style("font", "12px sans-serif");

const g = svg.append("g");

const linkgroup = g.append("g")
    .attr("fill", "none")
    .attr("stroke", "#555")
    .attr("stroke-opacity", 0.4)
    .attr("stroke-width", 1.5);

const nodegroup = g.append("g")
    .attr("stroke-linejoin", "round")
    .attr("stroke-width", 3);

function newdata(animate = true) {
    let root = tree(data);
    let links_data = root.links();
    let links = linkgroup
        .selectAll("path")
        .data(links_data, d => d.source.data.name + "_" + d.target.data.name);

    links.exit().remove();

    let newlinks = links
        .enter()
        .append("path");


    let t = d3.transition()
        .duration(animate ? 400 : 0)
        .ease(d3.easeLinear)
        .on("end", function() {
            const box = g.node().getBBox();
            svg.transition().duration(1000).attr("viewBox", `${box.x} ${box.y} ${box.width} ${box.height}`);
        });

    let alllinks = linkgroup.selectAll("path")
    alllinks
        .transition(t)
        .attr("d", d3.linkHorizontal()
            .x(d => d.y)
            .y(d => d.x));


    let nodes_data = root.descendants().reverse();
    let nodes = nodegroup
        .selectAll("g")
        .data(nodes_data, function(d) {
            if (d.parent) {
                return d.parent.data.name + d.data.name;
            }
            return d.data.name
        });

    nodes.exit().remove();

    let newnodes = nodes
        .enter().append("g");

    let allnodes = animate ? nodegroup.selectAll("g").transition(t) : nodegroup.selectAll("g");
    allnodes
        .attr("transform", d => `
        translate(${d.y},${d.x})
      `);

    newnodes.append("circle")
        .attr("r", 4.5)
        .on("click", function(d) {
            let altChildren = d.data.altChildren || [];
            let children = d.data.children;
            d.data.children = altChildren;
            d.data.altChildren = children;
            newdata();
        });

    nodegroup.selectAll("g circle").attr("fill", function(d) {
        let altChildren = d.data.altChildren || [];
        let children = d.data.children;
        return d.children || (children && (children.length > 0 || altChildren.length > 0)) ? "#555" : "#999"
    });

    newnodes.append("text")
        .attr("dy", "0.31em")
        .text(d => d.data.name)
        .clone(true).lower()
        .attr("stroke", "white");

    nodegroup.selectAll("g text")
        .attr("x", d => !d.children ? 6 : -6)
        .attr("text-anchor", d => !d.children ? "start" : "end")
    //.attr("transform", d => d.x >= Math.PI ? "rotate(180)" : null);

}

newdata(false);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<html>
<head>
    <title>Test</title>
</head>
<body>
    <div>
      <svg>
      </svg>
    </div>
</body>
</html>

This code is only producing a large single node instead of the horizontal collapsible tree.

I am trying to understand what should be changed in my code so that it works as a horizontal collapsible tree?

Syed Arefinul Haque
  • 1,123
  • 2
  • 14
  • 38

1 Answers1

1

The d3.json method uses the fetch api to return a Promise. Your code finishes running before the Promise resolves, so only the initial circle for the tree root draws. You need to handle the json call with async/await or then/catch.

d3.json("https://cdn.jsdelivr.net/gh/d3/d3-hierarchy@master/test/data/flare.json")
.then(json => {
  
     const data = json.data; // array of dates and values
     // d3 code for tree
})
.catch(error => {
    console.error(error);
});

See Getting data using d3.json() is not working, while it works using JS async await.. Why? for the async/await version.

Denise Mauldin
  • 5,397
  • 5
  • 32
  • 51
  • 1
    Thank you for the answer! Based on your answer I have created this jsfiddle which is working perfectly now: https://jsfiddle.net/syedarehaq/q4bkm3y0/ – Syed Arefinul Haque Mar 17 '21 at 02:25