Predrag Beocanin
Predrag Beocanin

Reputation: 1400

D3 layout force start with collapsed children

This is what I'm working with:

var data =

{
 name: "flare",
 children: [
  {name: "analytics", children:[{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"}]},
  {name: "analytics", children:[{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"}]},
  {name: "analytics", children:[{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"}]},
  {name: "analytics", children:[{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"}]},
  {name: "analytics", children:[{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"}]},
  {name: "analytics", children:[{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"}]},
  {name: "analytics", children:[{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"}]},
  {name: "analytics", children:[{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"},{name: "testera1"}]},

 ]
};

var width = 960,
    height = 500,
    root;

var force = d3.layout.force()
    .linkDistance(80)
    .charge(-120)
    .gravity(.05)
    .size([width, height])
    .on("tick", tick);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

var link = svg.selectAll(".link"),
    node = svg.selectAll(".node");



  root = data;
 
 update();
  


function update() {

  var nodes = flatten(root),
      links = d3.layout.tree().links(nodes);

  // Restart the force layout.
  force
      .nodes(nodes)
      .links(links)
      .start();

  // Update links.
  link = link.data(links, function(d) { return d.target.id; });

  link.exit().remove();

  link.enter().insert("line", ".node")
      .attr("class", "link");

  // Update nodes.
  node = node.data(nodes, function(d) { return d.id; });

  node.exit().remove();

  var nodeEnter = node.enter().append("g")
      .attr("class", "node")
      .on("click", click)
      .call(force.drag);

  nodeEnter.append("circle")
      .attr("r", function(d) { return Math.sqrt(d.size) / 10 || 4.5; });

  nodeEnter.append("text")
      .attr("dy", ".35em")
      .text(function(d) { return d.name; });

  node.select("circle")
      .style("fill", color);

}

function tick() {
  link.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; });

  node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
}

function color(d) {
  return d._children ? "#3182bd" // collapsed package
      : d.children ? "#c6dbef" // expanded package
      : "#fd8d3c"; // leaf node
}

// Toggle children on click.
function click(d) {
  if (d3.event.defaultPrevented) return; // ignore drag
  if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
  update();
}

// Returns a list of all nodes under the root.
function flatten(root) {
  var nodes = [], i = 0;

  function recurse(node) {
    if (node.children) node.children.forEach(recurse);
    if (!node.id) node.id = ++i;
    nodes.push(node);
  }

  recurse(root);
  return nodes;
}
.node circle {
  cursor: pointer;
  stroke: #3182bd;
  stroke-width: 1.5px;
}

.node text {
  font: 10px sans-serif;
  pointer-events: none;
  text-anchor: middle;
}

line.link {
  fill: none;
  stroke: #9ecae1;
  stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="//d3js.org/d3.v3.min.js"></script>

Basically, everything works fine, except this will start with all children expanded, but I need it to load with collapsed children. I've tried to run

 function collapse(d) {
                        if (d.children) {
                          d._children = d.children;
                          d._children.forEach(collapse);
                          d.children = null;
                        }

But it didn't help. I've tried to collapse them with:

function collapseAll(){
    root.children.forEach(collapse);
    collapse(root);
    update(root);
}

Didn't work neither.

Upvotes: 0

Views: 549

Answers (1)

Assan
Assan

Reputation: 428

The implementation of your collapse function seems to be correct. In your collapseAll function however you're calling collapse for each child of the root node, and also for the root node itself, thus recursing over the tree twice.

Here's a working fiddle.

Notice that the update function must be called once before collapsing the nodes, so that they are drawn.

Upvotes: 2

Related Questions