最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Organization chart - tree, online, dynamic, collapsible, pictures - in D3 - Stack Overflow

programmeradmin10浏览0评论

I am a noob in web-development. I'm trying to create a tree-like hierarchical company org chart. I tried both google's visualization chart and Mike Bostock's D3 Reingold tree.

I want these features :

  • tree structure : either top-down (google) or left-right (D3)
  • online/dynamic : viewable in browser and able to read data from json (both google & D3), not static visio or ppt diagram
  • collapsible : able to hide subtrees (both)
  • space-adjusting : nodes should fill visible area, to reduce scrolling (only D3)
  • attributes : display name, title & possibly picture (only google)

Above I've marked which tool allows which features, afaik.
I prefer the D3 version because it looks cool.
I can modify the .json to include additional fields (title, url to photo etc.) - here is a sample

My question is - how do I modify the D3 code to display an employee's name, then title in the next line, and maybe picture too ?

Or if that's not feasible - how do I modify the google code to automatically adjust spacing, so that all children of a node are close together, and I don't have to horizontally scroll ?

I am a noob in web-development. I'm trying to create a tree-like hierarchical company org chart. I tried both google's visualization chart and Mike Bostock's D3 Reingold tree.

I want these features :

  • tree structure : either top-down (google) or left-right (D3)
  • online/dynamic : viewable in browser and able to read data from json (both google & D3), not static visio or ppt diagram
  • collapsible : able to hide subtrees (both)
  • space-adjusting : nodes should fill visible area, to reduce scrolling (only D3)
  • attributes : display name, title & possibly picture (only google)

Above I've marked which tool allows which features, afaik.
I prefer the D3 version because it looks cool.
I can modify the .json to include additional fields (title, url to photo etc.) - here is a sample

My question is - how do I modify the D3 code to display an employee's name, then title in the next line, and maybe picture too ?

Or if that's not feasible - how do I modify the google code to automatically adjust spacing, so that all children of a node are close together, and I don't have to horizontally scroll ?

Share Improve this question edited Jun 19, 2015 at 1:41 d-_-b asked Jun 18, 2015 at 22:00 d-_-bd-_-b 8832 gold badges12 silver badges28 bronze badges 3
  • I don't know D3.js well enough to answer your question there. I do know the Google Charts API pretty well, though, and... Let's just say that based on the way you've presented your question that changing the spacing on the org chart is not something you want to attempt. The rendered org chart is actually just a very complicated HTML Table with some fancy CSS applied to it. It's not very flexible. It would take some pretty crazy CSS or jQuery voodoo to get it to do anything other than what it already does. – nbering Commented Jun 19, 2015 at 2:15
  • @Balrog30 - yes, I want google charts to adjust the spacing automatically - I rephrased the question here with code samples. – d-_-b Commented Jun 19, 2015 at 2:29
  • What I mean to say is, in order to do what you're describing you'd be writing something nearly as complex as what the Google Charts API is doing to draw what it is now. I don't know much about D3, but I know it's open source and extensible. Google Charts is not. You're probably better off with D3.js in this case. Google Charts is great and easy to use as long as you're willing to live with what Google is giving you for free. – nbering Commented Jun 19, 2015 at 2:38
Add a comment  | 

2 Answers 2

Reset to default 26

Here's a quick example. It modifies this example, to add in first name, last name, a title and a picture.

<!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="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>

var margin = {top: 20, right: 120, bottom: 20, left: 120},
    width = 960 - margin.right - margin.left,
    height = 300 - 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 data = {
  "fname": "Rachel",
  "lname": "Rogers",
  "title": "CEO",
  "photo": "http://lorempixel.com/60/60/cats/1",
  "children": [{
        "fname": "Bob",
        "lname": "Smith",
        "title": "President",
        "photo": "http://lorempixel.com/60/60/cats/2",
        "children": [{
              "fname": "Mary",
              "lname": "Jane",
              "title": "Vice President",
              "photo": "http://lorempixel.com/60/60/cats/3",
              "children": [{
                "fname": "Bill",
                "lname": "August",
                "title": "Dock Worker",
                "photo": "http://lorempixel.com/60/60/cats/4"
              }, {
                "fname": "Reginald",
                "lname": "Yoyo",
                "title": "Line Assembly",
                "photo": "http://lorempixel.com/60/60/cats/5"
              }]
            }, {
              "fname": "Nathan",
              "lname": "Ringwald",
              "title": "Comptroller",
              "photo": "http://lorempixel.com/60/60/cats/6"
            }]
  }]
}

root = data;
root.x0 = height / 2;
root.y0 = 0;

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

root.children.forEach(collapse);
update(root);

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);

  // add picture
  nodeEnter
    .append('defs')
    .append('pattern')
    .attr('id', function(d,i){
      return 'pic_' + d.fname + d.lname;
    })
    .attr('height',60)
    .attr('width',60)
    .attr('x',0)
    .attr('y',0)
    .append('image')
    .attr('xlink:href',function(d,i){
      return d.photo;
    })
    .attr('height',60)
    .attr('width',60)
    .attr('x',0)
    .attr('y',0);

  nodeEnter.append("circle")
      .attr("r", 1e-6)
      .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

  var g = nodeEnter.append("g");
  
  g.append("text")
      .attr("x", function(d) { return d.children || d._children ? -35 : 35; })
      .attr("dy", "1.35em")
      .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
      .text(function(d) { return d.fname + " " + d.lname; })
      .style("fill-opacity", 1e-6);
      
    g.append("text")
      .attr("x", function(d) { return d.children || d._children ? -35 : 35; })
      .attr("dy", "2.5em")
      .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
      .text(function(d) { return d.title; })
      .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", 30)
      .style("fill", function(d,i){
        return 'url(#pic_' + d.fname + d.lname+')';
      });

  nodeUpdate.selectAll("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>


Reversed Direction:

<!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="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>

var margin = {top: 20, right: 120, bottom: 20, left: 120},
    width = 960 - margin.right - margin.left,
    height = 300 - 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.x, d.y]; });

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 data = {
  "fname": "Rachel",
  "lname": "Rogers",
  "title": "CEO",
  "photo": "http://lorempixel.com/60/60/cats/1",
  "children": [{
        "fname": "Bob",
        "lname": "Smith",
        "title": "President",
        "photo": "http://lorempixel.com/60/60/cats/2",
        "children": [{
              "fname": "Mary",
              "lname": "Jane",
              "title": "Vice President",
              "photo": "http://lorempixel.com/60/60/cats/3",
              "children": [{
                "fname": "Bill",
                "lname": "August",
                "title": "Dock Worker",
                "photo": "http://lorempixel.com/60/60/cats/4"
              }, {
                "fname": "Reginald",
                "lname": "Yoyo",
                "title": "Line Assembly",
                "photo": "http://lorempixel.com/60/60/cats/5"
              }]
            }, {
              "fname": "Nathan",
              "lname": "Ringwald",
              "title": "Comptroller",
              "photo": "http://lorempixel.com/60/60/cats/6"
            }]
  }]
}

root = data;
root.x0 = height / 2;
root.y0 = 0;

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

root.children.forEach(collapse);
update(root);

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.x0 + "," + source.y0 + ")"; })
      .on("click", click);

  // add picture
  nodeEnter
    .append('defs')
    .append('pattern')
    .attr('id', function(d,i){
      return 'pic_' + d.fname + d.lname;
    })
    .attr('height',60)
    .attr('width',60)
    .attr('x',0)
    .attr('y',0)
    .append('image')
    .attr('xlink:href',function(d,i){
      return d.photo;
    })
    .attr('height',60)
    .attr('width',60)
    .attr('x',0)
    .attr('y',0);

  nodeEnter.append("circle")
      .attr("r", 1e-6)
      .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

  var g = nodeEnter.append("g");
  
  g.append("text")
      .attr("x", function(d) { return d.children || d._children ? -35 : 35; })
      .attr("dy", "1.35em")
      .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
      .text(function(d) { return d.fname + " " + d.lname; })
      .style("fill-opacity", 1e-6);
      
    g.append("text")
      .attr("x", function(d) { return d.children || d._children ? -35 : 35; })
      .attr("dy", "2.5em")
      .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
      .text(function(d) { return d.title; })
      .style("fill-opacity", 1e-6);

  // Transition nodes to their new position.
  var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

  nodeUpdate.select("circle")
      .attr("r", 30)
      .style("fill", function(d,i){
        return 'url(#pic_' + d.fname + d.lname+')';
      });

  nodeUpdate.selectAll("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.x + "," + source.y + ")"; })
      .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>

If you like to create your project with D3js, Just need to use the script codes from this and replaced3.json("/mbostock/raw/4063550/flare.json", function(error, flare) with this :

d3.json("yourJsonFile/jsonFileName.json", function(error, flare) 

The jsonFileName.json is your customized json file like this sample. Also you can insert name of your pictures into the json file and replace the img tag src with it.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论