| <!-- |
| Copyright 2010-2016 Mike Bostock |
| All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without modification, |
| are permitted provided that the following conditions are met: |
| |
| * Redistributions of source code must retain the above copyright notice, this |
| list of conditions and the following disclaimer. |
| |
| * Redistributions in binary form must reproduce the above copyright notice, |
| this list of conditions and the following disclaimer in the documentation |
| and/or other materials provided with the distribution. |
| |
| * Neither the name of the author nor the names of contributors may be used to |
| endorse or promote products derived from this software without specific prior |
| written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR |
| ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| http://bl.ocks.org/mbostock/4339083 |
| --> |
| <!DOCTYPE html> |
| <meta charset="utf-8"> |
| <style> |
| |
| .node { |
| cursor: pointer; |
| } |
| |
| .node circle { |
| fill: #fff; |
| stroke: steelblue; |
| stroke-width: 1.5px; |
| } |
| |
| .node text { |
| font: 10px sans-serif; |
| } |
| |
| .link { |
| fill: none; |
| stroke: #ccc; |
| stroke-width: 1.5px; |
| } |
| |
| </style> |
| <body> |
| <script src="http://d3js.org/d3.v3.min.js"></script> |
| <script> |
| |
| var margin = {top: 20, right: 100, bottom: 20, left: 200}, |
| width = 1500 - margin.right - margin.left, |
| height = 800 - margin.top - margin.bottom; |
| |
| var i = 0, |
| duration = 750, |
| root; |
| |
| var tree = d3.layout.tree() |
| .size([height, width]); |
| |
| var diagonal = d3.svg.diagonal() |
| .projection(function(d) { return [d.y, d.x]; }); |
| |
| var svg = d3.select("body").append("svg") |
| .attr("width", width + margin.right + margin.left) |
| .attr("height", height + margin.top + margin.bottom) |
| .append("g") |
| .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
| |
| var treeData = [ $MYJSONHIERARCHY ]; |
| |
| root = treeData[0]; |
| root.x0 = height / 2; |
| root.y0 = 0; |
| |
| function collapse(d) { |
| if (d.children) { |
| d._children = d.children; |
| d._children.forEach(collapse); |
| d.children = null; |
| } |
| } |
| |
| update(root); |
| |
| d3.select(self.frameElement).style("height", "800px"); |
| |
| function update(source) { |
| |
| // Compute the new tree layout. |
| var nodes = tree.nodes(root).reverse(), |
| links = tree.links(nodes); |
| |
| // Normalize for fixed-depth. |
| nodes.forEach(function(d) { d.y = d.depth * 180; }); |
| |
| // Update the nodes… |
| var node = svg.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") |
| .attr("class", "node") |
| .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; }) |
| .on("click", click); |
| |
| nodeEnter.append("circle") |
| .attr("r", 1e-6) |
| .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }); |
| |
| nodeEnter.append("text") |
| .attr("x", function(d) { return d.children || d._children ? -10 : 10; }) |
| .attr("dy", ".35em") |
| .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; }) |
| .text(function(d) { return d.name; }) |
| .style("fill-opacity", 1e-6); |
| |
| // Transition nodes to their new position. |
| var nodeUpdate = node.transition() |
| .duration(duration) |
| .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; }); |
| |
| nodeUpdate.select("circle") |
| .attr("r", 4.5) |
| .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }); |
| |
| nodeUpdate.select("text") |
| .style("fill-opacity", 1); |
| |
| // 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", 1e-6); |
| |
| nodeExit.select("text") |
| .style("fill-opacity", 1e-6); |
| |
| // Update the links |
| var link = svg.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; |
| }); |
| } |
| |
| // Toggle children on click. |
| function click(d) { |
| if (d.children) { |
| d._children = d.children; |
| d.children = null; |
| } else { |
| d.children = d._children; |
| d._children = null; |
| } |
| update(d); |
| } |
| |
| </script> |