I am quite new to d3.js and javascript. I just want to try out some d3 sample code.
I copied over the d3.js code from here.
<!DOCTYPE html>
<meta charset="utf-8" />
<style>
.node circle {
fill: #999;
}
.node text {
font: 10px sans-serif;
}
.node--internal circle {
fill: #555;
}
.node--internal text {
text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
}
.link {
fill: none;
stroke: rgb(214, 15, 145);
stroke-opacity: 0.4;
stroke-width: 1px;
}
form {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
label {
display: block;
}
</style>
<svg width="928" height="928"></svg>
<script src="http://d3js.org/d3.v6.min.js"></script>
<script>
const data = {
name: "Name",
children: [
{
name: "A_F",
children: [
{
name: "Alice",
},
{
name: "Bob",
},
{
name: "Charlie",
},
],
},
{
name: "G_P",
children: [
{
name: "Gary",
},
{
name: "Helen",
},
],
},
],
};
// Specify the chart’s dimensions.
const width = 928;
const height = width;
const radius = width / 6;
// Create the color scale.
const color = d3.scaleOrdinal(d3.quantize(d3.interpolateRainbow, data.children.length + 1));
// Compute the layout.
const hierarchy = d3.hierarchy(data)
.sum(d => d.value)
.sort((a, b) => b.value - a.value);
const root = d3.partition()
.size([2 * Math.PI, hierarchy.height + 1])
(hierarchy);
root.each(d => d.current = d);
// Create the arc generator.
const arc = d3.arc()
.startAngle(d => d.x0)
.endAngle(d => d.x1)
.padAngle(d => Math.min((d.x1 - d.x0) / 2, 0.005))
.padRadius(radius * 1.5)
.innerRadius(d => d.y0 * radius)
.outerRadius(d => Math.max(d.y0 * radius, d.y1 * radius - 1))
// Create the SVG container.
const svg = d3.create("svg")
.attr("viewBox", [-width / 2, -height / 2, width, width])
.style("font", "10px sans-serif");
// Append the arcs.
const path = svg.append("g")
.selectAll("path")
.data(root.descendants().slice(1))
.join("path")
.attr("fill", d => { while (d.depth > 1) d = d.parent; return color(d.data.name); })
.attr("fill-opacity", d => arcVisible(d.current) ? (d.children ? 0.6 : 0.4) : 0)
.attr("pointer-events", d => arcVisible(d.current) ? "auto" : "none")
.attr("d", d => arc(d.current));
// Make them clickable if they have children.
path.filter(d => d.children)
.style("cursor", "pointer")
.on("click", clicked);
const format = d3.format(",d");
path.append("title")
.text(d => `${d.ancestors().map(d => d.data.name).reverse().join("/")}\n${format(d.value)}`);
const label = svg.append("g")
.attr("pointer-events", "none")
.attr("text-anchor", "middle")
.style("user-select", "none")
.selectAll("text")
.data(root.descendants().slice(1))
.join("text")
.attr("dy", "0.35em")
.attr("fill-opacity", d => +labelVisible(d.current))
.attr("transform", d => labelTransform(d.current))
.text(d => d.data.name);
const parent = svg.append("circle")
.datum(root)
.attr("r", radius)
.attr("fill", "none")
.attr("pointer-events", "all")
.on("click", clicked);
// Handle zoom on click.
function clicked(event, p) {
parent.datum(p.parent || root);
root.each(d => d.target = {
x0: Math.max(0, Math.min(1, (d.x0 - p.x0) / (p.x1 - p.x0))) * 2 * Math.PI,
x1: Math.max(0, Math.min(1, (d.x1 - p.x0) / (p.x1 - p.x0))) * 2 * Math.PI,
y0: Math.max(0, d.y0 - p.depth),
y1: Math.max(0, d.y1 - p.depth)
});
const t = svg.transition().duration(750);
// Transition the data on all arcs, even the ones that aren’t visible,
// so that if this transition is interrupted, entering arcs will start
// the next transition from the desired position.
path.transition(t)
.tween("data", d => {
const i = d3.interpolate(d.current, d.target);
return t => d.current = i(t);
})
.filter(function(d) {
return +this.getAttribute("fill-opacity") || arcVisible(d.target);
})
.attr("fill-opacity", d => arcVisible(d.target) ? (d.children ? 0.6 : 0.4) : 0)
.attr("pointer-events", d => arcVisible(d.target) ? "auto" : "none")
.attrTween("d", d => () => arc(d.current));
label.filter(function(d) {
return +this.getAttribute("fill-opacity") || labelVisible(d.target);
}).transition(t)
.attr("fill-opacity", d => +labelVisible(d.target))
.attrTween("transform", d => () => labelTransform(d.current));
}
function arcVisible(d) {
return d.y1 <= 3 && d.y0 >= 1 && d.x1 > d.x0;
}
function labelVisible(d) {
return d.y1 <= 3 && d.y0 >= 1 && (d.y1 - d.y0) * (d.x1 - d.x0) > 0.03;
}
function labelTransform(d) {
const x = (d.x0 + d.x1) / 2 * 180 / Math.PI;
const y = (d.y0 + d.y1) / 2 * radius;
return `rotate(${x - 90}) translate(${y},0) rotate(${x < 180 ? 0 : 180})`;
}
svg.node();
</script>
The only thing I edit is to remove the last return svg.node();
and change it to svg.node();
When I spin up a local http server (py3 -m http.server 8000
), I found that there is nothing shown in the browser and there is no error when I click the inspect
.
Just want to learn how to triage this issue and trouble shoot. Thank you!
Unfortunately, D3 canonical examples are generally found on Observable, which while interactive and useful, doesn't translate to regular 'ol javascript without a change or two.
First, the return svg.node()
is used by Observable to return a detached DOM element in order to display it. The detached node is created with d3.create(). This won't work in a vanilla javascript environment: normally, you'd append the SVG directly with d3.select("parentElement").append("svg")
or select it if already exists, as in your code (d3.select("svg")
).
However, your example has a data format where the leaf nodes (nodes that have no children themselves) have a value property:
"children":[{"name":"AgglomerativeCluster","value":3938},{"name":"CommunityStructure","value":3812}
While your data does not:
children: [{ name: "Alice", }, { name: "Bob", },
This matters as the hierarchy uses a sum function to tabulate the relative sizes of each node:
const hierarchy = d3.hierarchy(data)
.sum(d => d.value)
So you could add a value property to each leaf node, which will generate a visual. However, if each leaf should have the same weighting, then you can use:
const hierarchy = d3.hierarchy(data)
.count()
Where each parent will be weighted according to the number of leaf nodes in each branch.