title: Is there a way to automatically open a D3 collapsed tree node by node (for nodes that have children)?
body: <p>I would like to know if there is a way to let nodes with children automatically open up once the page is loaded. I did some digging, but wasn't able to find any information on that. My sample tree code is below:</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;!DOCTYPE html&gt;
  &lt;meta charset="utf-8"&gt;
  &lt;style&gt; 
  .node {
    cursor: pointer;
  }
   .overlay{
    background-color:#FFF;
  }
   .node text {
    font-size: 0.85em;
  font-family: 'Roboto Condensed', sans-serif;
    font-weight: 500;}
  
   .link {
    fill: none;
    stroke:#bcbcbc;
    stroke-width:1px;
  }
   
  .templink {
    fill: none;
    stroke: red;
    stroke-width: 3px;
  }
    .ghostCircle.show{
    display:block;
  }
    .ghostCircle, .activeDrag .ghostCircle{
    display: none;
  }
  &lt;/style&gt; &lt;script src="http://code.jquery.com/jquery-1.10.2.min.js"&gt;&lt;/script&gt;
  &lt;script src="http://d3js.org/d3.v3.min.js"&gt;&lt;/script&gt;
   &lt;head&gt;
  
                &lt;link href='https://fonts.googleapis.com/css?family=Amaranth' rel='stylesheet' type='text/css'&gt;
  
                &lt;link href='https://fonts.googleapis.com/css?family=Playfair+Display' rel='stylesheet' type='text/css'&gt;
  
       &lt;link href='https://fonts.googleapis.com/css?family=Josefin+Sans' rel='stylesheet' type='text/css'&gt;
  
                &lt;link href='https://fonts.googleapis.com/css?family=Fjord+One' rel='stylesheet' type='text/css'&gt;
  
                &lt;link href='https://fonts.googleapis.com/css?family=Lateef' rel='stylesheet' type='text/css'&gt;
  
                &lt;link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'&gt;
  
                &lt;link href='https://fonts.googleapis.com/css?family=Lato' rel='stylesheet' type='text/css'&gt;
  
                &lt;link href='https://fonts.googleapis.com/css?family=Roboto+Condensed' rel='stylesheet' type='text/css'&gt;
  
                &lt;link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro' rel='stylesheet' type='text/css'&gt;
  
                &lt;link href='https://fonts.googleapis.com/css?family=Ubuntu' rel='stylesheet' type='text/css'&gt;
  
                 &lt;/head&gt;
   &lt;body&gt;
    &lt;div id="tree-container"&gt;&lt;/div&gt;
    &lt;script&gt;
   var treeData = {"name": "Parent", "_children": [{"name": "Child 1", "_children": [{"name": "Grandchild 1"}, {"name": "Grandchild 2"}]}, {"name": "Child 2", "_children": [{"name": "Grandchild 3"}, {"name": "Grandchild 4"}]}]};
   // Calculate total nodes, max label length
      var totalNodes = 0;
      var maxLabelLength = 0;
      // variables for drag/drop
      var selectedNode = null;
      var draggingNode = null;
      // panning variables
      var panSpeed = 200;
      var panBoundary = 20; // Within 20px from edges will pan when dragging.
      // Misc. variables
      var i = 0;
      var duration = 750;
      var root;
  
      // size of the diagram
      var viewerWidth = $(document).width();
      var viewerHeight = $(document).height();
  
      var tree = d3.layout.tree()
          .size([viewerHeight, viewerWidth]);
  
      // define a d3 diagonal projection for use by the node paths later on.
      var diagonal = d3.svg.diagonal()
          .projection(function(d) {
              return [d.y, d.x];
          });
  
      // A recursive helper function for performing some setup by walking through all nodes
  
      function visit(parent, visitFn, childrenFn) {
          if (!parent) return;
  
          visitFn(parent);
  
          var children = childrenFn(parent);
          if (children) {
              var count = children.length;
              for (var i = 0; i &lt; count; i++) {
                  visit(children[i], visitFn, childrenFn);
              }
          }
      }
  
      // Call visit function to establish maxLabelLength
      visit(treeData, function(d) {
          totalNodes++;
          maxLabelLength = Math.max(d.name.length, maxLabelLength);
  
      }, function(d) {
          return d.children &amp;&amp; d.children.length &gt; 0 ? d.children : null;
      });
  
  
      // sort the tree according to the node names
  
      function sortTree() {
          tree.sort(function(a, b) {
              return b.name.toLowerCase() &lt; a.name.toLowerCase() ? 1 : -1;
          });
      }
      // Sort the tree initially incase the JSON isn't in a sorted order.
      sortTree();
      function pan(domNode, direction) {
          var speed = panSpeed;
          if (panTimer) {
              clearTimeout(panTimer);
              translateCoords = d3.transform(svgGroup.attr("transform"));
              if (direction == 'left' || direction == 'right') {
                  translateX = direction == 'left' ? translateCoords.translate[0] + speed : translateCoords.translate[0] - speed;
                  translateY = translateCoords.translate[1];
              } else if (direction == 'up' || direction == 'down') {
                  translateX = translateCoords.translate[0];
                  translateY = direction == 'up' ? translateCoords.translate[1] + speed : translateCoords.translate[1] - speed;
              }
              scaleX = translateCoords.scale[0];
              scaleY = translateCoords.scale[1];
              scale = zoomListener.scale();
              svgGroup.transition().attr("transform", "translate(" + translateX + "," + translateY + ")scale(" + scale + ")");
              d3.select(domNode).select('g.node').attr("transform", "translate(" + translateX + "," + translateY + ")");
              zoomListener.scale(zoomListener.scale());
              zoomListener.translate([translateX, translateY]);
              panTimer = setTimeout(function() {
                  pan(domNode, speed, direction);
              }, 50);
          }
      }
  
      // Define the zoom function for the zoomable tree
  
      function zoom() {
          svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
      }
      // define the zoomListener which calls the zoom function on the "zoom" event constrained within the scaleExtents
      var zoomListener = d3.behavior.zoom().scaleExtent([0.1, 3]).on("zoom", zoom);
  
      function initiateDrag(d, domNode) {
          draggingNode = d;
          d3.select(domNode).select('.ghostCircle').attr('pointer-events', 'none');
          d3.selectAll('.ghostCircle').attr('class', 'ghostCircle show');
          d3.select(domNode).attr('class', 'node activeDrag');
  
          svgGroup.selectAll("g.node").sort(function(a, b) { // select the parent and sort the path's
              if (a.id != draggingNode.id) return 1; // a is not the hovered element, send "a" to the back
              else return -1; // a is the hovered element, bring "a" to the front
          });
          // if nodes has children, remove the links and nodes
          if (nodes.length &gt; 1) {
              // remove link paths
              links = tree.links(nodes);
              nodePaths = svgGroup.selectAll("path.link")
                  .data(links, function(d) {
                      return d.target.id;
                  }).remove();
              // remove child nodes
              nodesExit = svgGroup.selectAll("g.node")
                  .data(nodes, function(d) {
                      return d.id;
                  }).filter(function(d, i) {
                      if (d.id == draggingNode.id) {
                          return false;
                      }
                      return true;
                  }).remove();
          }
  
          // remove parent link
          parentLink = tree.links(tree.nodes(draggingNode.parent));
          svgGroup.selectAll('path.link').filter(function(d, i) {
              if (d.target.id == draggingNode.id) {
                  return true;
              }
              return false;
          }).remove();
  
          dragStarted = null;
      }
  
      // define the baseSvg, attaching a class for styling and the zoomListener
      var baseSvg = d3.select("#tree-container").append("svg")
          .attr("width", viewerWidth)
          .attr("height", viewerHeight)
          .attr("class", "overlay")
          .call(zoomListener);
      // Define the drag listeners for drag/drop behaviour of nodes.
      dragListener = d3.behavior.drag()
          .on("dragstart", function(d) {
              if (d == root) {
                  return;
              }
              dragStarted = true;
              nodes = tree.nodes(d);
              d3.event.sourceEvent.stopPropagation();
              // it's important that we suppress the mouseover event on the node being dragged. Otherwise it will absorb the mouseover event and the underlying node will not detect it d3.select(this).attr('pointer-events', 'none');
          })
          .on("drag", function(d) {
              if (d == root) {
                  return;
              }
              if (dragStarted) {
                  domNode = this;
                  initiateDrag(d, domNode);
              }
  
              // get coords of mouseEvent relative to svg container to allow for panning
              relCoords = d3.mouse($('svg').get(0));
              if (relCoords[0] &lt; panBoundary) {
                  panTimer = true;
                  pan(this, 'left');
              } else if (relCoords[0] &gt; ($('svg').width() - panBoundary)) {
  
                  panTimer = true;
                  pan(this, 'right');
              } else if (relCoords[1] &lt; panBoundary) {
                  panTimer = true;
                  pan(this, 'up');
              } else if (relCoords[1] &gt; ($('svg').height() - panBoundary)) {
                  panTimer = true;
                  pan(this, 'down');
              } else {
                  try {
                      clearTimeout(panTimer);
                  } catch (e) {
  
                  }
              }
  
              d.x0 += d3.event.dy;
              d.y0 += d3.event.dx;
              var node = d3.select(this);
              node.attr("transform", "translate(" + d.y0 + "," + d.x0 + ")");
              updateTempConnector();
          }).on("dragend", function(d) {
              if (d == root) {
                  return;
              }
              domNode = this;
              if (selectedNode) {
                  // now remove the element from the parent, and insert it into the new elements children
                  var index = draggingNode.parent.children.indexOf(draggingNode);
                  if (index &gt; -1) {
                      draggingNode.parent.children.splice(index, 1);
                  }
                  if (typeof selectedNode.children !== 'undefined' || typeof selectedNode._children !== 'undefined') {
                      if (typeof selectedNode.children !== 'undefined') {
                          selectedNode.children.push(draggingNode);
                      } else {
                          selectedNode._children.push(draggingNode);
                      }
                  } else {
                      selectedNode.children = [];
                      selectedNode.children.push(draggingNode);
                  }
                  // Make sure that the node being added to is expanded so user can see added node is correctly moved
                  expand(selectedNode);
                  sortTree();
                  endDrag();
              } else {
                  endDrag();
              }
          });
  
      function endDrag() {
          selectedNode = null;
          d3.selectAll('.ghostCircle').attr('class', 'ghostCircle');
          d3.select(domNode).attr('class', 'node');
          // now restore the mouseover event or we won't be able to drag a 2nd time
          d3.select(domNode).select('.ghostCircle').attr('pointer-events', '');
          updateTempConnector();
          if (draggingNode !== null) {
              update(root);
              centerNode(draggingNode);
              draggingNode = null;
          }
      }
  
      // Helper functions for collapsing and expanding nodes.
  
      function collapse(d) {
          if (d.children) {
              d._children = d.children;
              d._children.forEach(collapse);
              d.children = null;
          }
      }
  
      function expand(d) {
          if (d._children) {
              d.children = d._children;
              d.children.forEach(expand);
              d._children = null;
          }
      }
  
      var overCircle = function(d) {
          selectedNode = d;
          updateTempConnector();
      };
      var outCircle = function(d) {
          selectedNode = null;
          updateTempConnector();
      };
  
      // Function to update the temporary connector indicating dragging affiliation
      var updateTempConnector = function() {
          var data = [];
          if (draggingNode !== null &amp;&amp; selectedNode !== null) {
              // have to flip the source coordinates since we did this for the existing connectors on the original tree
              data = [{
                  source: {
                      x: selectedNode.y0,
                      y: selectedNode.x0
                  },
                  target: {
                      x: draggingNode.y0,
                      y: draggingNode.x0
                  }
              }];
          }
          var link = svgGroup.selectAll(".templink").data(data);
  
          link.enter().append("path")
              .attr("class", "templink")
              .attr("d", d3.svg.diagonal())
              .attr('pointer-events', 'none');
  
          link.attr("d", d3.svg.diagonal());
  
          link.exit().remove();
      };
  
      // Function to center node when clicked/dropped so node doesn't get lost when collapsing/moving with large amount of children.
  
      function centerNode(source) {
          scale = zoomListener.scale();
          x = -source.y0;
          y = -source.x0;
          x = x * scale + viewerWidth / 2;
          y = y * scale + viewerHeight / 2;
          d3.select('g').transition()
              .duration(duration)
              .attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")");
          zoomListener.scale(scale);
          zoomListener.translate([x, y]);
      }
  
      // Toggle children function
  
      function toggleChildren(d) {
          if (d.children) {
              d._children = d.children;
              d.children = null;
          } else if (d._children) {
              d.children = d._children;
              d._children = null;
          }
          return d;
      }
  
      // Toggle children on click.
  
      function click(d) {
          if (d3.event.defaultPrevented) return; // click suppressed
          d = toggleChildren(d);
          update(d);
          centerNode(d);
      }     function update(source) {
          // Compute the new height, function counts total children of root node and sets tree height accordingly.
          // This prevents the layout looking squashed when new nodes are made visible or looking sparse when nodes are removed
          // This makes the layout more consistent.
          var levelWidth = [1];
          var childCount = function(level, n) {
  
              if (n.children &amp;&amp; n.children.length &gt; 0) {
                  if (levelWidth.length &lt;= level + 1) levelWidth.push(0);
  
                  levelWidth[level + 1] += n.children.length;
                  n.children.forEach(function(d) {
                      childCount(level + 1, d);
                  });
              }
          };         childCount(0, root);
          var newHeight = d3.max(levelWidth) * 40;
          tree = tree.size([newHeight, viewerWidth]);
  
          // Compute the new tree layout.
          var nodes = tree.nodes(root).reverse(),
              links = tree.links(nodes);
  
          // Set widths between levels based on maxLabelLength.
          nodes.forEach(function(d) {
              d.y = (d.depth * (maxLabelLength * 30)); //maxLabelLength * 10px
              // alternatively to keep a fixed scale one can set a fixed depth per level
              // Normalize for fixed-depth by commenting out below line
              // d.y = (d.depth * 500); //500px per level.
          });
  
          // Update the nodes…
          node = svgGroup.selectAll("g.node")
              .data(nodes, function(d) {
                  return d.id || (d.id = ++i);
              });
  
          // Enter any new nodes at the parent's previous position.
          var nodeEnter = node.enter().append("g")
              .call(dragListener)
              .attr("class", "node")
              .attr("transform", function(d) {
                  return "translate(" + source.y0 + "," + source.x0 + ")";
              })
              .on('click', click);
  
          nodeEnter.append("circle")
              .attr('class', 'nodeCircle')
              .attr("r", 4.5)
              .style("stroke-width", "1")
              .style("stroke", "red")
              .style("fill", function(d) {
                  return d._children ? "#EEB4B4" : "#FFF";
              });        
  
          // phantom node to give us mouseover in a radius around it
          nodeEnter.append("circle")
              .attr("class", "ghostCircle")
              .attr("r",30)
              .attr("opacity", 0.2) // change this to zero to hide the target area
              .style("fill", "#EEB4B4")
              .attr("pointer-events", "mouseover")
              .on("mouseover", function(node) {
                  overCircle(node);
              })
              .on("mouseout", function(node) {
                  outCircle(node);
              });   var textGroup = nodeEnter.append( "g" );
    var rects = textGroup.append( "rect" );
    var texts = textGroup.append( "text" )
                         .text( function( d ) { return d.name; } )
                         .on( "mouseover", function( d ){
                              d3.select( this )
                              .transition( )
                              .duration( 100 )
                              .attr( "fill", "red" );
                         })
                         .on( "mouseout", function( d ){
                            d3.select( this )
                              .transition( )
                              .duration( 100 )
                              .attr( "fill", "black"  );
                          })
                         .each( function( d ) { d.width = this.getBBox( ).width; } )
                         .attr( "text-anchor", function( d ) { return d.children || d._children ? "end" : "start"; })
                         .attr( "x", function( d ) { return d.children || d._children ? -10 : 10; })
                         .attr( "dy", "0.30em" )
                         .style( "fill-opacity", 1e-6 );
  
    rects.attr( "x" , function( d ) { return d.children || d._children ? -10 - d.width: 10; })
         .attr( "y", "-0.5em" )
         .attr( "height" , "1em" )
         .style( "fill", "#FFF")
         .style( "fill-opacity", 1e-6  )
         .attr( "width" , function( d ) { return d.width; });
  
  
          // Update the text to reflect whether node has children or not.
          node.select("text")
              .attr("x", function(d) {
                  return d.children || d._children ? -10 : 10;
              })
              .attr("text-anchor", function(d) {
                  return d.children || d._children ? "end" : "start";
              })
              .text(function(d) {
                  return d.name;
              });
  
          // Change the circle fill depending on whether it has children and is collapsed
          node.select("circle.nodeCircle")
              .attr("r", 4.5)
              .style("fill", function(d) {
                  return d._children ? "#EEB4B4" : "#FFF";
              });
  
   
          // Transition nodes to their new position.
          var nodeUpdate = node.transition()
              .duration(duration)
              .attr("transform", function(d) {
                  return "translate(" + d.y + "," + d.x + ")";
              });
  
          // Fade the text in
          nodeUpdate.select("text")
              .style("fill-opacity", 1);
          nodeUpdate.select("rect")
              .style("fill-opacity", 0.9);
  
          // Transition exiting nodes to the parent"s new position.
          var nodeExit = node.exit().transition()
              .duration(duration)
              .attr("transform", function(d) {
                  return "translate(" + source.y + "," + source.x + ")";
              })
              .remove();
  
          nodeExit.select("circle")
              .attr("r", 0);
  
          nodeExit.select("text")
              .style("fill-opacity", 0);
          nodeExit.select("rect")
              .style("fill-opacity", 0);
  
          // Update the links…
          var link = svgGroup.selectAll("path.link")
              .data(links, function(d) {
                  return d.target.id;
              });
  
          // Enter any new links at the parent"s previous position.
          link.enter().insert("path", "g")
              .attr("class", "link")
              .attr("d", function(d) {
                  var o = {
                      x: source.x0,
                      y: source.y0
                  };
                  return diagonal({
                      source: o,
                      target: o
                  });
              });
  
          // Transition links to their new position.
          link.transition()
              .duration(duration)
              .attr("d", diagonal);
  
          // Transition exiting nodes to the parent"s new position.
          link.exit().transition()
              .duration(duration)
              .attr("d", function(d) {
                  var o = {
                      x: source.x,
                      y: source.y
                  };
                  return diagonal({
                      source: o,
                      target: o
                  });
              })
              .remove();
  
          // Stash the old positions for transition.
          nodes.forEach(function(d) {
              d.x0 = d.x;
              d.y0 = d.y;
          });
      }
  
      // Append a group which holds all nodes and which the zoom Listener can act upon.
      var svgGroup = baseSvg.append("g");
  
      // Define the root
      root = treeData;
      root.x0 = viewerHeight / 2;
      root.y0 = 0;
  
      // Layout the tree initially and center on the root node.
      update(root);
      centerNode(root);
  //});
  
  &lt;/script&gt; &lt;/body&gt;
  &lt;/html&gt;</code></pre>
  </div>
  </div>
  </p>