D3: Hyperlinks in tree diagram
d3.js, hyperlink, javascript, json
Solution
Seems you've made a slight mistake when creating your dataset url attribute, or you should've adapted the code you're using to add the xlink:href attribute to the a element. You're calling:
.attr("xlink:href", function(d) { return d.url; })
But in your dataset the attribute defined like this:
"URL": "http://www.google.co.uk"
Those don't match, you should change your code to this:
.attr("xlink:href", function(d) { return d.URL; })
Or your dataset to this:
"url": "http://www.google.co.uk"
Remember that names of JSON properties are case-sensitive.
Problem
I'm having trouble hyperlinking child elements in a Reingold-Tifold tree graph. The graph is built in D3 and is based on the one that Mike Bostock uses an example: http://bl.ocks.org/mbostock/4339184 My code and a segment of my JSON file are below. At this point, the graph works fine - if I click on a parent node, it expands to reveal a series of child nodes that are contained in my JSON file. But when I get to the child level, I want to make the word hyperlinked. I don't understand how to do that. I've used a previous SO question/answer - Hyperlinks in d3.js objects - to understand how to add a URL to my JSON data (at this point everything is just linking to Google.co.uk). But I don't understand how to link that URL to my tree graph so that the child nodes become hyperlinked. Can you let me know how I can do this? Thank you, in advance, for any help. (And thanks, @IH8, for your assistance so far.) Matt ``` <html xmlns:xlink="http://www.w3.org/1999/xlink"> <head><meta charset="utf-8"> <style> .node { cursor: pointer; } .node circle { fill: #FF0000; stroke: black; stroke-width: 1.5px; } .node text { font: 11px sans-serif; } .link { fill: none; stroke: #FF0000; stroke-width: 1.5px; } </style> <style id="style-1-cropbar-clipper">/* Copyright 2014 Evernote Corporation. All rights reserved. */ .en-markup-crop-options { top: 18px !important; left: 50% !important; margin-left: -100px !important; width: 200px !important; border: 2px rgba(255,255,255,.38) solid !important; border-radius: 4px !important; } .en-markup-crop-options div div:first-of-type { margin-left: 0px !important; } </style></head><body> <script src="http://d3js.org/d3.v3.min.js"></script> <script> var margin = {top: 20, right: 120, bottom: 20, left: 120}, width = 960 - 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 + ")"); d3.json("/sites/default/files/tcs/D3/data/turbo.json", function(error, turbo) { root = turbo; 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); }); 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 ? "red" : "#fff"; }); nodeEnter.each(function(d){ var thisNode = d3.select(this); if (!d.children) { thisNode.append("a") .attr("xlink:href", function(d) { return d.url; }) .append("text") .attr("x", 8) .attr("dy", 3) .attr("text-anchor", "start") .text(function(d) { return d.name; }); } else { thisNode.append("text") .attr("x", -8) .attr("dy", 3) .attr("text-anchor", "end") .text(function(d) { return d.name; }); } }); // 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 ? "red" : "#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> </body> </html> ``` A portion of my JSON file: ``` { "name": "turbo", "children": [ { "name": "Level 1", "children": [ { "name": "Google", "children": [ {"name": "Website", "size": 3938, "URL":"http://www.google.co.uk"}, {"name": "CommunityStructure", "size": 3812, "URL":"http://www.google.co.uk"}, {"name": "HierarchicalCluster", "size": 6714, "URL":"http://www.google.co.uk"}, {"name": "MergeEdge", "size": 743, "URL":"http://www.google.co.uk"} ] }, { "name": "graph", "children": [ {"name": "BetweennessCentrality", "size": 3534, "URL":"http://www.google.co.uk"}, {"name": "LinkDistance", "size": 5731, "URL":"http://www.google.co.uk"}, {"name": "MaxFlowMinCut", "size": 7840, "URL":"http://www.google.co.uk"}, {"name": "ShortestPaths", "size": 5914, "URL":"http://www.google.co.uk"}, {"name": "SpanningTree", "size": 3416, "URL":"http://www.google.co.uk"} ] }, { "name": "optimization", "children": [ {"name": "AspectRatioBanker", "size": 7074, "URL":"http://www.google.co.uk"} ] } ] }, . . . ```