I am a complete beginner in d3 and have been stuck with a problem for weeks.
Based on the example: https://jsfiddle.net/t4vzg650/6/ I have developed a collapsible force layout with d3. I have successfully managed to add links and audios (only work locally at the moment). But by default, I only want the best practice, tools and course structure nodes to be expanded and the other nodes to be collapsed. They can then be opened on demand with a double click. I have gone through similar questions here, but I have not managed to implement it.
var data = {
"name": "Best Practices",
"size": 50,
"color": "#C37B89",
"font": 15,
"children": [{
"name": "Course Structure",
"size": 50,
"color": "#BCCC9A",
"font": 15,
"_children": null,
"children": [{
"name": "x",
"size": 30,
"color": "#BCCC9A",
"font": 8,
"_children": null,
"children" : [{
"name": "x",
"size": 30,
"color": "#BCCC9A",
"font": 8,
}, {
"name": "x",
"size": 30,
"color": "#BCCC9A",
"font": 8,
}
]
}, {
"name": "x",
"size": 30,
"color": "#BCCC9A",
"font": 8,
}]
}, {
"name": "Tools",
"size": 50,
"color": "#EAE7C6",
"font": 15,
"_children": null,
"children": [{
"name": "x",
"size": 30,
"color": "#EAE7C6",
"font": 8
}, {
"name": "x",
"size": 30,
"color": "#EAE7C6",
"font": 8
}]
}]
};
var i = 0;
var root = d3.hierarchy(data);
var nodeSvg, linkSvg, nodeEnter, linkEnter;
var width = 960
var height = 600
var centerx = width/2
var centery = height/2
var svg = d3.select("#network")
.attr("width", width)
.attr("height", height)
.append("g");
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}).distance(100))
.force("charge", d3.forceManyBody().strength(-300))
//center network
.force("center", d3.forceCenter(centerx, centery))
.on("tick", ticked);
update();
function update() {
var nodes = flatten(root);
var links = root.links()
linkSvg = svg.selectAll(".link")
.data(links, function(d) {
return d.target.id;
})
linkSvg.exit().remove();
var linkEnter = linkSvg.enter()
.append("line")
.attr("class", "link");
linkSvg = linkEnter.merge(linkSvg)
nodeSvg = svg.selectAll(".node")
.data(nodes, function(d) {
return d.id;
})
nodeSvg.exit().remove();
var nodeEnter = nodeSvg.enter()
.append("g")
.attr("class", "node")
.on("dblclick", click)
.call(d3.drag()
.on("end", dragended))
nodeEnter.append("circle")
.attr("r", function(d) {return d.data.size})
.attr("fill", function(d) {return d.data.color})
nodeEnter.append("text")
.text(function(d) {
return d.data.name;})
.attr("font-size", function(d) {return d.data.font})
.attr("class", "headline")
nodeSvg = nodeEnter.merge(nodeSvg);
simulation
.nodes(nodes)
simulation.force("link")
.links(links);
}
function ticked() {
linkSvg
.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
nodeSvg
.attr("transform", function(d) {
return "translate(" + d.x + ", " + d.y + ")";
});
}
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
update();
simulation.restart();
} else {
d.children = d._children;
d._children = null;
update();
simulation.restart();
}
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0)
d.fx = null
d.fy = null
}
function flatten(root) {
var nodes = [];
function recurse(node) {
if (node.children) node.children.forEach(recurse);
if (!node.id) node.id = ++i;
else ++i;
nodes.push(node);
}
recurse(root);
return nodes;
}
My code is here: https://codepen.io/LaraB1612/pen/bGreEWV
I would be so happy if someone could help me with this.
Thank you very much
I have figured out how to implement this:
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update();
}
var flattenedChildOne = flatten(root.children[0]);
var flattenedChildTwo = flatten(root.children[1]);
flattenedChildOne.forEach(function(d) {
d._children = d.children;
d.children = null;
});
flattenedChildTwo.forEach(function(d) {
d._children = d.children;
d.children = null;
});
update();
"_children": null
from your data, they were unnecessary.I have solved this in here: https://jsfiddle.net/orsisi/tnv37wxr/39/