Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
410 views
in Technique[技术] by (71.8m points)

javascript - D3.js Titles on Collapsible Force-Directed graph

So i've been working on a collapsible force-directed graph based of the following example.

Im trying to advance from that and add titles to each node. I've followed a similar answer here on stackoverflow, but I cannot implement the solution from that answer into the example above and other similar solutions.

Please can someone point me in the correct direction.

See project code below before implementation.

enter image description here

JS

var w = 600,
    h = 600,
    radius = 10,
    node,
    link,
    root;

var force = d3.layout.force()
    .on("tick", tick)
    .charge(function(d) { return -500; })
    .linkDistance(function(d) { return d.target._children ? 100 : 50; })
    .size([w, h - 160]);

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

root = words[0]; //set root node
root.fixed = true;
root.x = w / 2;
root.y = h / 2 - 80;
update();

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

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

    // Update the links…
    link = svg.selectAll(".link")
        .data(links);

    // Enter any new links.
    link.enter().insert("svg:line", ".node")
        .attr("class", "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; });

    // Exit any old links.
    link.exit().remove();

    // Update the nodes…
    node = svg.selectAll("circle.node")
        .data(nodes)
        .style("fill", color);

    node.transition()
        .attr("r", radius);

    node.append("title")
        .text(function(d) { return d.name; });

    // Enter any new nodes.
    node.enter().append("svg:circle")
        .attr("class", "node")
        .attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; })
        .attr("r", radius)
        .style("fill", color)
        .on("click", click)
        .call(force.drag);

    // Exit any old nodes.
    node.exit().remove();
}

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("cx", function(d) { return d.x; })
          .attr("cy", function(d) { return d.y; });
}

// Color leaf nodes orange, and packages white or blue.
function color(d) {
    if(d._children){
        return "#95a5a6";
    }else{
        switch(d.group) {
            case 'r': //adverb
                return "#e74c3c";
                break;
            case 'n': //noun
                return "#3498db";
                break;
            case 'v': //verb
                return "#2ecc71";
                break;
            case 's': //adjective
                return "#e78229";
                break;
            default:
                return "#9b59b6";
        }
    }
}

// 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();
}

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

    function recurse(node) {
        if (node.children) node.size = node.children.reduce(function(p, v) { return p + recurse(v); }, 0);
        if (!node.id) node.id = ++i;
        nodes.push(node);
        return node.size;
    }

    root.size = recurse(root);
    return nodes;
}

CSS

circle.node {
    cursor: pointer;
    stroke: #34495e;
    stroke-width: 2px;
    box-sizing: border-box;
    stroke-location: inside;
}

line.link {
    fill: none;
    stroke: #34495e;
    stroke-width: 1.5px;
}

HTML

<!DOCTYPE html>
<body>
<script src="http://d3js.org/d3.v2.min.js?2.9.6"></script>
    <script>

        var words = [
    {
        "group":"n",
        "word":"main node",
        "children":[
            {
                "group":"n",
                "name":"sub node 1"
            },
            {
                "group":"n",
                "name":"sub node 2"
            },
            {
                "group":"n",
                "name":"sub node 3"
            },
            {
                "group":"v",
                "name":"sub node 4"
            },
            {
                "group":"s",
                "name":"sub node 5"
            },
            {
                "group":"s",
                "name":"sub node 6"
            },
            {
                "group":"s",
                "name":"sub node 7"
            },
            {
                "group":"s",
                "name":"sub node 8"
            },
            {
                "group":"s",
                "name":"sub node 9"
            },
            {
                "group":"s",
                "name":"sub node 10"
            },
            {
                "group":"s",
                "name":"sub node 11"
            },
            {
                "group":"r",
                "name":"sub node 12",
                "children":[
                    {
                        "group":"r",
                        "name":"sub sub node 1"
                    },
                    {
                        "group":"r",
                        "name":"sub sub node 2"
                    },
                    {
                        "group":"r",
                        "name":"sub sub node 3"
                    }
                ]
            }
        ]
    }
]
    </script>
</body>

JSFiddle

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Interestingly, it is kind of working as a snippet here with your code as is, but only after you've collapsed or expanded a node. This should give you some idea of where your problem is. Basically, you're adding the title element to each node before the nodes actually exist. Move the

node.append("title")
    .text(function(d) { return d.name; });

to after the

node.enter().append("svg:circle")
    .attr("class", "node")
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; })
    .attr("r", radius)
    .style("fill", color)
    .on("click", click)
    .call(force.drag);

call.

Here is a snippet with that change completed:

var words = [{
  "group": "n",
  "word": "main node",
  "children": [{
    "group": "n",
    "name": "sub node 1"
  }, {
    "group": "n",
    "name": "sub node 2"
  }, {
    "group": "n",
    "name": "sub node 3"
  }, {
    "group": "v",
    "name": "sub node 4"
  }, {
    "group": "s",
    "name": "sub node 5"
  }, {
    "group": "s",
    "name": "sub node 6"
  }, {
    "group": "s",
    "name": "sub node 7"
  }, {
    "group": "s",
    "name": "sub node 8"
  }, {
    "group": "s",
    "name": "sub node 9"
  }, {
    "group": "s",
    "name": "sub node 10"
  }, {
    "group": "s",
    "name": "sub node 11"
  }, {
    "group": "r",
    "name": "sub node 12",
    "children": [{
      "group": "r",
      "name": "sub sub node 1"
    }, {
      "group": "r",
      "name": "sub sub node 2"
    }, {
      "group": "r",
      "name": "sub sub node 3"
    }]
  }]
}]


var w = 600,
  h = 600,
  radius = 10,
  node,
  link,
  root;

var force = d3.layout.force()
  .on("tick", tick)
  .charge(function(d) {
    return -500;
  })
  .linkDistance(function(d) {
    return d.target._children ? 100 : 50;
  })
  .size([w, h - 160]);

var svg = d3.select("#viz").append("svg")
  .attr("width", w)
  .attr("height", h);

root = words[0]; //set root node
root.fixed = true;
root.x = w / 2;
root.y = h / 2 - 80;
update();

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

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

  // Update the links…
  link = svg.selectAll(".link")
    .data(links);

  // Enter any new links.
  link.enter().insert("svg:line", ".node")
    .attr("class", "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;
    });

  // Exit any old links.
  link.exit().remove();

  // Update the nodes…
  node = svg.selectAll("circle.node")
    .data(nodes)
    .style("fill", color);

  node.transition()
    .attr("r", radius);


  // Enter any new nodes.
  node.enter().append("svg:circle")
    .attr("class", "node")
    .attr("cx", function(d) {
      return d.x;
    })
    .attr("cy", function(d) {
      return d.y;
    })
    .attr("r", radius)
    .style("fill", color)
    .on("click", click)
    .call(force.drag);

  node.append("title")
    .text(function(d) {
      return d.name;
    });


  // Exit any old nodes.
  node.exit().remove();
}

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("cx", function(d) {
      return d.x;
    })
    .attr("cy", function(d) {
      return d.y;
    });
}

// Color leaf nodes orange, and packages white or blue.
function color(d) {
  if (d._children) {
    return "#95a5a6";
  } else {
    switch (d.group) {
      case 'r': //adverb
        return "#e74c3c";
        break;
      case 'n': //noun
        return "#3498db";
        break;
      case 'v': //verb
        return "#2ecc71";
        break;
      case 's': //adjective
        return "#e78229";
        break;
      default:
        return "#9b59b6";
    }
  }
}

// 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();
}

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

  function recurse(node) {
    if (node.children) node.size = node.children.reduce(function(p, v) {
      return p + recurse(v);
    }, 0);
    if (!node.id) node.id = ++i;
    nodes.push(node);
    return node.size;
  }

  root.size = recurse(root);
  return nodes;
}
circle.node {
  cursor: pointer;
  stroke: #34495e;
  stroke-width: 2px;
  box-sizing: border-box;
  stroke-location: inside;
}
line.link {
  fill: none;
  stroke: #34495e;
  stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="viz"></div>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...