I have a previous question which solves a problem with paging of many child nodes
There are few problems though with implementing in my d3.js context:
The first and last child nodes that initiate the paging are not named
'...' according to the script functionality. This appears to be a v7 to v3.5 issue.
The paging should only occur on the leaf nodes, not any middle
branches.
It needs to work with d3.js v3.5 (yes I know, but this is pending
migrating the tree to v7...). Basically the main issue is with naming of the first and last paging nodes...
See fiddle for implementation and issues in d3.js v3.5
Relevant from previous solution:
function pageNodes(d, maxNode) {
if (d.children) {
d.children.forEach(c => pageNodes(c, maxNode));
if (d.children.length > maxNode) {
d.pages = {}
const count = maxNode - 2;
const l = Math.ceil(d.children.length / count);
for (let i = 0; i < l; i++) {
let startRange = i * count;
let endRange = i * count + count;
d.pages[i] = d.children.slice(startRange, endRange);
d.pages[i].unshift({
...d.pages[i][0],
data: {
name: "..."
},
page: i == 0 ? l - 1 : i - 1
})
// console.log(i, d.pages[i]);
d.pages[i].push({
...d.pages[i][0],
data: {
name: "..."
},
page: i != (l - 1) ? i + 1 : 0,
});
}
d.children = d.pages[0];
console.log(d.pages)
}
}
}
root.children.forEach(c => pageNodes(c, 8));
function click(d) {
if (d.hasOwnProperty('page')) {
d.parent.children = d.parent.pages[d.page];
} else if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
Notes
The order is mandatory pageNodes function needs to be called before collapse function call, As collapse changes the children key to _children and pageNodes function works with children key
I created pageNodes function in a way so it does not require calling again and again which would waste computation power, so we call it once on start and the structure is adjusted as per requirement then we just work with click function without requiring to call pageNodes again and again.
I have modified the pageNodes function to only page if data contain page: true, for reference you can check unit-group data segment.
Example 1 - require adding page key in data to enable paging for selective data segment.
var treeData = {"name":"Program","column_to_sort_by":null,"type":"program","children":[{"name":"ProgramGroup1","column_to_sort_by":null,"type":"program_group","children":[{"name":"POGroup1","column_to_sort_by":null,"type":"1program_outcome_group","children":[{"name":"PO1","column_to_sort_by":null,"type":"program_outcome","children":[{"name":"Unit1","column_to_sort_by":"Unit1","children":[{"name":"UG1-LE","column_to_sort_by":null,"page":true,"type":"unit_group","children":[{"name":"LE1","column_to_sort_by":"LE1","type":"learning_event"},{"name":"LE10","column_to_sort_by":"LE10","type":"learning_event"},{"name":"LE11","column_to_sort_by":"LE11","type":"learning_event"},{"name":"LE12","column_to_sort_by":"LE12","type":"learning_event"},{"name":"LE13","column_to_sort_by":"LE13","type":"learning_event"},{"name":"LE14","column_to_sort_by":"LE14","type":"learning_event"},{"name":"LE15","column_to_sort_by":"LE15","type":"learning_event"},{"name":"LE2","column_to_sort_by":"LE2","type":"learning_event"},{"name":"LE4","column_to_sort_by":"LE4","type":"learning_event"},{"name":"LE5","column_to_sort_by":"LE5","type":"learning_event"},{"name":"LE6","column_to_sort_by":"LE6","type":"learning_event"},{"name":"LE7","column_to_sort_by":"LE7","type":"learning_event"},{"name":"LE8","column_to_sort_by":"LE8","type":"learning_event"},{"name":"LE9","column_to_sort_by":"LE9","type":"learning_event"}]},{"name":"UG1-Assessments","column_to_sort_by":null,"page":true,"type":"unit_group","children":[{"name":"ASST1","column_to_sort_by":"ASST1","type":"assessment"},{"name":"ASST10","column_to_sort_by":"ASST10","type":"assessment"},{"name":"ASST11","column_to_sort_by":"ASST11","type":"assessment"},{"name":"ASST13","column_to_sort_by":"ASST13","type":"assessment"},{"name":"ASST14","column_to_sort_by":"ASST14","type":"assessment"},{"name":"ASST15","column_to_sort_by":"ASST15","type":"assessment"},{"name":"ASST2","column_to_sort_by":"ASST2","type":"assessment"},{"name":"ASST3","column_to_sort_by":"ASST3","type":"assessment"},{"name":"ASST4","column_to_sort_by":"ASST4","type":"assessment"},{"name":"ASST5","column_to_sort_by":"ASST5","type":"assessment"},{"name":"ASST6","column_to_sort_by":"ASST6","type":"assessment"},{"name":"ASST7","column_to_sort_by":"ASST7","type":"assessment"},{"name":"ASST8","column_to_sort_by":"ASST8","type":"assessment"},{"name":"ASST9","column_to_sort_by":"ASST9","type":"assessment"}]}],"type":"unit"}]},{"name":"PO2","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO3","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO4","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO5","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO6","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO7","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO8","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO9","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO10","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO11","column_to_sort_by":null,"type":"program_outcome"}]},{"name":"POGroup2","column_to_sort_by":null,"type":"1program_outcome_group"}]},{"name":"ProgramGroup2","column_to_sort_by":null,"type":"program_group"}]};
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = 2000 - margin.right - margin.left,
height = 500 - 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 + ")");
root = treeData;
root.x0 = height / 2;
root.y0 = 0;
function pageNodes(d, options) {
if (d.children) {
d.children.forEach(c => pageNodes(c, options));
if (d.page && d.children.length > options.maxNode) {
d.pages = {}
const count = options.maxNode - 2;
const l = Math.ceil(d.children.length / count);
for (let i = 0; i < l; i++) {
const startRange = i * count;
const endRange = i * count + count;
let pageNumber = i == 0 ? l - 1 : i - 1;
d.pages[i] = d.children.slice(startRange, endRange);
d.pages[i].unshift({
...d.pages[i][0],
data: {
name: options.getLabel ? options.getLabel(pageNumber) : "..."
},
pageNumber,
name: "..."
})
// console.log(i, d.pages[i]);
pageNumber = i != (l - 1) ? i + 1 : 0;
d.pages[i].push({
...d.pages[i][0],
data: {
name: options.getLabel ? options.getLabel(pageNumber) : "..."
},
pageNumber,
name: "..."
});
}
d.children = d.pages[0];
console.log(d.pages)
}
}
}
root.children.forEach(c => pageNodes(c, {
maxNode: 8,
}));
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
//svg.style("height", "500px");
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)
.attr('stroke', function(d) {
return d.color ? d.color : 'blue';
})
.style("fill", function(d) {
return d._children ? "#ccc" : "#fff";
});
nodeEnter.append("text")
.attr("x", function(d) {
return d.children || d._children ? -13 : 13;
})
.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", 10)
.style("fill", function(d) {
var collapseColor = d.color ? d.color : '#ccc';
return d._children ? collapseColor : "#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.hasOwnProperty('pageNumber')) {
d.parent.children = d.parent.pages[d.pageNumber];
} else if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
Here working example
Example 2 - through page options which is a function which would return true/false to enable/disable paging for selective data segment.
var treeData = {"name":"Program","column_to_sort_by":null,"type":"program","children":[{"name":"ProgramGroup1","column_to_sort_by":null,"type":"program_group","children":[{"name":"POGroup1","column_to_sort_by":null,"type":"1program_outcome_group","children":[{"name":"PO1","column_to_sort_by":null,"type":"program_outcome","children":[{"name":"Unit1","column_to_sort_by":"Unit1","children":[{"name":"UG1-LE","column_to_sort_by":null,"type":"unit_group","children":[{"name":"LE1","column_to_sort_by":"LE1","type":"learning_event"},{"name":"LE10","column_to_sort_by":"LE10","type":"learning_event"},{"name":"LE11","column_to_sort_by":"LE11","type":"learning_event"},{"name":"LE12","column_to_sort_by":"LE12","type":"learning_event"},{"name":"LE13","column_to_sort_by":"LE13","type":"learning_event"},{"name":"LE14","column_to_sort_by":"LE14","type":"learning_event"},{"name":"LE15","column_to_sort_by":"LE15","type":"learning_event"},{"name":"LE2","column_to_sort_by":"LE2","type":"learning_event"},{"name":"LE4","column_to_sort_by":"LE4","type":"learning_event"},{"name":"LE5","column_to_sort_by":"LE5","type":"learning_event"},{"name":"LE6","column_to_sort_by":"LE6","type":"learning_event"},{"name":"LE7","column_to_sort_by":"LE7","type":"learning_event"},{"name":"LE8","column_to_sort_by":"LE8","type":"learning_event"},{"name":"LE9","column_to_sort_by":"LE9","type":"learning_event"}]},{"name":"UG1-Assessments","column_to_sort_by":null,"type":"unit_group","children":[{"name":"ASST1","column_to_sort_by":"ASST1","type":"assessment"},{"name":"ASST10","column_to_sort_by":"ASST10","type":"assessment"},{"name":"ASST11","column_to_sort_by":"ASST11","type":"assessment"},{"name":"ASST13","column_to_sort_by":"ASST13","type":"assessment"},{"name":"ASST14","column_to_sort_by":"ASST14","type":"assessment"},{"name":"ASST15","column_to_sort_by":"ASST15","type":"assessment"},{"name":"ASST2","column_to_sort_by":"ASST2","type":"assessment"},{"name":"ASST3","column_to_sort_by":"ASST3","type":"assessment"},{"name":"ASST4","column_to_sort_by":"ASST4","type":"assessment"},{"name":"ASST5","column_to_sort_by":"ASST5","type":"assessment"},{"name":"ASST6","column_to_sort_by":"ASST6","type":"assessment"},{"name":"ASST7","column_to_sort_by":"ASST7","type":"assessment"},{"name":"ASST8","column_to_sort_by":"ASST8","type":"assessment"},{"name":"ASST9","column_to_sort_by":"ASST9","type":"assessment"}]}],"type":"unit"}]},{"name":"PO2","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO3","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO4","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO5","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO6","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO7","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO8","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO9","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO10","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO11","column_to_sort_by":null,"type":"program_outcome"}]},{"name":"POGroup2","column_to_sort_by":null,"type":"1program_outcome_group"}]},{"name":"ProgramGroup2","column_to_sort_by":null,"type":"program_group"}]};
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = 2000 - margin.right - margin.left,
height = 500 - 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 + ")");
root = treeData;
root.x0 = height / 2;
root.y0 = 0;
function pageNodes(d, options) {
if (d.children) {
d.children.forEach(c => pageNodes(c, options));
if (options.page && options.page(d) && d.children.length > options.maxNode) {
d.pages = {}
const count = options.maxNode - 2;
const l = Math.ceil(d.children.length / count);
for (let i = 0; i < l; i++) {
const startRange = i * count;
const endRange = i * count + count;
let pageNumber = i == 0 ? l - 1 : i - 1;
d.pages[i] = d.children.slice(startRange, endRange);
d.pages[i].unshift({
...d.pages[i][0],
data: {
name: options.getLabel ? options.getLabel(pageNumber) : "..."
},
pageNumber,
name: "..."
})
// console.log(i, d.pages[i]);
pageNumber = i != (l - 1) ? i + 1 : 0;
d.pages[i].push({
...d.pages[i][0],
data: {
name: options.getLabel ? options.getLabel(pageNumber) : "..."
},
pageNumber,
name: "..."
});
}
d.children = d.pages[0];
console.log(d.pages)
}
}
}
root.children.forEach(c => pageNodes(c, {
maxNode: 8,
page: function(d) {
return d.type == "unit_group";
}
}));
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
//svg.style("height", "500px");
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)
.attr('stroke', function(d) {
return d.color ? d.color : 'blue';
})
.style("fill", function(d) {
return d._children ? "#ccc" : "#fff";
});
nodeEnter.append("text")
.attr("x", function(d) {
return d.children || d._children ? -13 : 13;
})
.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", 10)
.style("fill", function(d) {
var collapseColor = d.color ? d.color : '#ccc';
return d._children ? collapseColor : "#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.hasOwnProperty('pageNumber')) {
d.parent.children = d.parent.pages[d.pageNumber];
} else if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
Here working example
I am trying to display d3 collapsable tree with search, here is the reference example am using [Search tree][1]
[1]: https://bl.ocks.org/jjzieve/a743242f46321491a950 I am getting error select2() is not a function. I did not get any solution on v5, how to change the above example based on my code.
Here is my code
function searchTree(obj, search, path) {
if (obj.name === search) { //if search is found return, add the object to the path and return it
path.push(obj);
return path;
}
else if (obj.children || obj._children) { //if children are collapsed d3 object will have them instantiated as _children
var children = (obj.children) ? obj.children : obj._children;
for (var i = 0; i < children.length; i++) {
path.push(obj);// we assume this path is the right one
var found = searchTree(children[i], search, path);
if (found) {// we were right, this should return the bubbled-up path from the first if statement
return found;
}
else {//we were wrong, remove this parent from the path and continue iterating
path.pop();
}
}
}
else {//not the right object, return false so it will continue to iterate in the loop
return false;
}
}
function extract_select2_data(node, leaves, index) {
if (node.children) {
for (var i = 0; i < node.children.length; i++) {
index = extract_select2_data(node.children[i], leaves, index)[0];
}
}
else {
leaves.push({ id: ++index, text: node.name });
}
return [index, leaves];
}
console.log("left side hitting ");
// Set the dimensions and margins of the diagram
var margin = { top: 20, right: 120, bottom: 20, left: 120 },
width = 1280 * 10 - margin.right - margin.left,
height = 1200 *10 - margin.top - margin.bottom;
// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select(".col-md-6").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 i = 0,
duration = 750,
root, path,select2_data;
// declares a tree layout and assigns the size
var treemap = d3.tree().size([height, width]);
function openPaths(paths) {
for (var i = 0; i < paths.length; i++) {
if (paths[i].id !== "1") {//i.e. not root
paths[i].class = 'found';
if (paths[i]._children) { //if children are hidden: open them, otherwise: don't do anything
paths[i].children = paths[i]._children;
paths[i]._children = null;
}
update(paths[i]);
}
}
}
// Assigns parent, children, height, depth
root = d3.hierarchy(this.parentTreeObj, function (d) { return d.children });
select2_data = extract_select2_data(this.parentTreeObj,[],0)[1];
root.x0 = height / 2;
root.y0 = 0;
// Collapse after the second level
root.children.forEach(collapse);
update(root);
$(".col-md-6").select2({
data: select2_data,
containerCssClass: "search"
});
//attach search box listener
$(".col-md-6").on("select2-selecting", function (e) {
var paths = searchTree(root, e.object.text, []);
if (typeof (paths) !== "undefined") {
openPaths(paths);
}
else {
alert(e.object.text + " not found!");
}
})
d3.select(self.frameElement).style("height", "800px");
// Collapse the node and all it's children
function collapse(d) {
if (d.children) {
d._children = d.children
d._children.forEach(collapse)
d.children = null
}
}
function update(source) {
var duration = d3.event && d3.event.altKey ? 5000 : 500;
// compute the new height
var levelWidth = [1];
var childCount = function (level, n) {
if (n.children && n.children.length > 0) {
if (levelWidth.length <= 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) * 20;
treemap = d3.tree().size([newHeight, width]);
// Assigns the x and y position for the nodes
var treeData = treemap(root);
// Compute the new tree layout.
var nodes = treeData.descendants(),
links = treeData.descendants().slice(1);
// Normalize for fixed-depth.
nodes.forEach(function (d) { d.y = (d.depth * 180); });
// ****************** Nodes section ***************************
// Update the nodes...
var node = svg.selectAll('g.node')
.data(nodes, function (d) {
return d.id || (d.id = ++i);
});
// Enter any new modes 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 Circle for the nodes
nodeEnter.append('circle')
.attr('class', 'node')
.attr('r', 1e-6)
.style("fill", function (d) {
return d._children ? "#eb8c00" : "#da5506";
});
// Add labels for the nodes
nodeEnter.append('text')
.attr("dy", ".35em")
.attr("x", function (d) {
return d.children || d._children ? -13 : 13;
})
.attr("text-anchor", function (d) {
return d.children || d._children ? "end" : "start";
})
.text(function (d) {
return d.data.name;
});
// UPDATE
var nodeUpdate = nodeEnter.merge(node);
// Transition to the proper position for the node
nodeUpdate.transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + d.y + "," + d.x + ")";
});
// Update the node attributes and style
nodeUpdate.select('circle.node')
.attr('r', 10)
.style("fill", function (d) {
return d._children || d._id ? "#eb8c00" : "#da5506";
})
.attr('cursor', 'pointer');
// Remove any exiting nodes
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
// On exit reduce the node circles size to 0
nodeExit.select('circle')
.attr('r', 1e-6);
// On exit reduce the opacity of text labels
nodeExit.select('text')
.style('fill-opacity', 1e-6);
// ****************** links section ***************************
// Update the links...
var link = svg.selectAll('path.link')
.data(links, function (d) { return d.id; });
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.attr('d', function (d) {
var o = { x: source.x0, y: source.y0 }
return diagonal(o, o)
});
// UPDATE
var linkUpdate = linkEnter.merge(link);
// Transition back to the parent element position
linkUpdate.transition()
.duration(duration)
.attr('d', function (d) { return diagonal(d, d.parent) });
// Remove any exiting links
var linkExit = link.exit().transition()
.duration(duration)
.attr('d', function (d) {
var o = { x: source.x, y: source.y }
return diagonal(o, o)
})
.remove();
// Store the old positions for transition.
nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
// Creates a curved (diagonal) path from parent to the child nodes
function diagonal(s, d) {
console.log("left side hitting inside ");
console.log("in Left");
// console.log("idddd",d.data.id);
path = `M ${s.y} ${s.x}
C ${(s.y + d.y) / 2} ${s.x},
${(s.y + d.y) / 2} ${d.x},
${d.y} ${d.x}`
return path
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
// d._id = d.data.id;
// d.data.id = null;
} else {
d.children = d._children;
d._children = null;
// d.data.id = d._id;
// d._id = null;
}
update(d);
}
}
Can anyone please help me out this. Thanks in advance
Have you linked your file to the select2 library? For example, do you have this:
<link rel="stylesheet" type="text/css" href="http://cdnjs.cloudflare.com/ajax/libs/select2/3.5.2/select2.min.css"></link>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/select2/3.5.2/select2.min.js"></script>
If you could share a link through JSFiddle, bl.ocks, or in a repository, that would help out a lot too. Let me know if that helps.
i am working in d3 ,rotating cluster layout ..i want to resize the cluster layout based on number of child nodes..
based on my code ,when i open the parent with minimum child ,there is no problem
when i open a parent that contains more child,there is some overlapping ,so want to reduce the cluster size further,when there is more number of child in the parent...how can i do this??
my entire code is
myJSON= http://pastebin.com/vZ32jGQc
treeData = myJSON;
var selectedNode = null;
var draggingNode = null;
var panSpeed = 200;
var panBoundary = 0;
var i = 0;
var duration = 750;
var root;
var width = 5000;
var height = 5000;
var diameter = 750;
var tree = d3.layout.tree().size([360, diameter / 2 - 120])
.separation(function (a, b) {
return (a.parent == b.parent ? 1 : 5) / a.depth;
});
var diagonal = d3.svg.diagonal.radial()
.projection(function (d) {
return [d.y, d.x / 180 * Math.PI];
});
root = treeData;
root.x0 = height / 2;
root.y0 = 0;
function sortTree() {
tree.sort(function (a, b) {
return b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1;
});
}
sortTree();
var baseSvg = d3.select("#tree-container").append("svg")
.attr("width", 1200)
.attr("height",1200)
.attr("class", "overlay")
.attr("transform", "translate(" + 1000 + "," + 1000 + ")");
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
update(d);
}
function expand(d) {
if (d._children) {
d.children = d._children;
d.children.forEach(expand);
d._children = null;
}
}
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;
}
function click(d) {
if(!d.parent){
return;
}
if (!d.children)
myJSON.children.forEach(collapse);
if (d3.event.defaultPrevented) return;
d = toggleChildren(d);
update(d);
}
function update(source) {
var levelWidth = [1];
var childCount = function (level, n) {
if (n.children && n.children.length > 0) {
if (levelWidth.length <= level + 1) levelWidth.push(0);
levelWidth[level + 1] += n.children.length;
n.children.forEach(function (d) {
childCount(level + 1, d);
});
}
};
childCount(0, root);
var nodes = tree.nodes(root);
links = tree.links(nodes);
node = svgGroup.selectAll("g.node")
.data(nodes, function (d) {
return d.id || (d.id = ++i);
});
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.on('click', click)
nodeEnter.append("circle")
.attr("class", "smallcircle")
.style("stroke", function(d) {
return d.color;
})
nodeEnter.append("text")
.text(function (d) {
return d.name;
})
// .style("font", "12px serif")
.style("opacity", 1)
.style("fill-opacity", 0)
.on("mouseover", function (d) {
var r = d3.select(this).node().getBoundingClientRect();
d3.select("div#tooltip")
.style("display", "inline")
.style("top", (r.top-25) + "px")
.style("top", 10 + "px")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 40) + "px")
.style("left", r.left + 40+"px")
.style("left", + "px")
.style("position", "absolute")
.text(d.t);
})
.on("mouseout", function(){
d3.select("div#tooltip").style("display", "none")
})
node.select("circle.nodeCircle")
.attr("r", 4.5)
.style("fill", function (d) {
return d._children ? "red" : "#fff";
});
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function (d) {
return "rotate(" + (d.x - 90) + ")
translate(" + d.y + ")rotate(" + (-d.x + 90) + ")";
});
nodeUpdate.select("circle")
.attr("r", 4.5)
.style("fill", function (d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeUpdate.select("text")
.style("fill-opacity", 9)
.attr("fill",function(d){return (d.children?"red":"black");})
.attr("font-size",function(d)
{return (d.children?"20px":"12px");})
.attr("dy", ".35em")
.attr("text-anchor", function (d) {
return d.x < 180 ? "start" : "end";
})
.attr("transform", function (d) {
return d.x < 180 ? "translate(8)"
: "rotate(360)translate(-8)";
});
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + source.x + "," + source.y + ")";
})
.remove();
nodeExit.select("circle")
.attr("r", 0);
nodeExit.select("text")
.style("fill-opacity", 0);
var link = svgGroup.selectAll("path.link")
.data(links, function (d) {
return d.target.id;
})
link.style("stroke", function(d) {
return d.color;
})
link.enter().insert("path", "g")
.attr("class", "link")
link.style("stroke", function(d) {
return d.target.color;
})
.attr("d", function (d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
});
link.transition()
.duration(duration)
.attr("d", diagonal);
link.exit().transition()
.duration(duration)
.attr("d", function (d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
var svgGroup = baseSvg.append("g")
.attr("transform", "translate(" + 550 + "," + 300 + ")")
d3.selectAll("text").style("fill", function (d)
{ return d3.select(this).classed(d.cond, true); })
root.children.forEach(function (child) {
collapse(child);
});
update(root);
d3.select(self.frameElement).style("height", width);
So I've built a tree that will collapse/expand with animations using d3's library. I'm still very new to d3 and I'm not sure how to add my specific styling to the fray. Any suggestions on how to style <g> tags?
Here is what I want it to look like:
And here is what it currently looks like:
Here is the relevant code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
</style>
<body>
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = $(window).width() - margin.right - margin.left,
height = $(window).height() - 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];
});
buildJson(function (data) {
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 + ")");
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);
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 * 350;
});
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 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 ? -12 : 12;
})
.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;
});
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Remove Root Node, ultra mega important!!!
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
node.each(function(d){
if (d.name == "flare")
d3.select(this).remove();});
link.each(function(d){
if (d.source.name == "flare")
d3.select(this).remove();});
}
// 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;
}
// If the node has a parent, then collapse its child nodes
// except for this clicked node.
if (d.parent) {
d.parent.children.forEach(function(element) {
if (d !== element) {
collapse(element);
}
});
}
update(d);
}
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
});
// Get Data and format it properly for presentation
function getJsonData (pathToData, callback) {
$.getJSON( pathToData, function() {
console.log( "Retrieving: " + pathToData );
}).done(function(data) {
callback({
error: false,
data: data
});
})
.fail(function() {
callback({
error: true
});
});
}
function buildJson (builder) {
var runOnce = true, categories_raw, technologies_raw, formatted_json = {
'name' : 'flare',
'children' : []
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// First get all of the categories and subcategories
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
getJsonData('assets/json/categories.json', function (data) {
if (!data.error) {
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Categories and Subcategories Retreived now build it out
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
categories_raw = data.data
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Technologies retreived now build it out
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
getJsonData('assets/json/technologies.json', function (data) {
if (!data.error) {
technologies_raw = data.data
var categories = categories_raw[0], subcategories = categories_raw[1], technologies = technologies_raw, formatted_categories = [], formatted_subcategories = [], formatted_technologies = []
subcategories.forEach ( function (value, index) {
formatted_subcategories.push({
'name' : value.label,
'id' : value.id,
'children' : [],
'parent' : value.parent
})
})
technologies.forEach ( function (value, index) {
var result = formatted_subcategories.filter(function( obj ) {
return obj.id == value.subCat;
});
if (typeof result[0] != 'undefined') {
result[0].children.push({
'name' : value.label
})
}
})
categories.forEach ( function (value, index) {
var result = formatted_subcategories.filter(function( obj ) {
return obj.parent == value.id;
});
formatted_json.children.push({
'name' : value.label,
'id' : value.id,
'children' : result
})
})
builder(formatted_json)
}
})
}
})
}
</script>
Well even though this was downvoted I still think it is important that an answer exists.
https://gist.github.com/Quixomatic/f39d338bb4b4790d38f7d08b52f34b98
This link points to a gist that shows my solution to the problem. Thanks for the help, no one.
Google Chrome console is returning the following error:
Error: Problem parsing d="M296.20961279999995,NaNC462.51521919999993,NaN 311.0200934399999,NaN 477.3256998399999,NaN"
Posted is my code:
var taskflows = [
{
"description" : "Taskflow #1",
"taskExecutions" : [
{"description" : "First","step" : 1},
{"description" : "Second","step" : 2},
{"description" : "Second", "step" : 2},
{"description" : "Third", "step" : 3},
{"description" : "Fourth", "step" : 4},
{"description" : "Fifth","step" : 5},
{"description" : "Sixth", "step" : 6},
{"description" : "Sixth", "step" : 6},
{"description" : "Seventh","step" : 7},
{"description" : "Eighth", "step" : 8},
{"description" : "Ninth", "step" : 9},
{"description" : "Tenth", "step" : 10},
{"description" : "Eleventh", "step" : 11},
{"description" : "Twelve", "step" : 12},
{"description" : "Twelve","step" : 12}
]
}];
var taskflowTree = function (taskflow) {
var tree = taskflow.map(function(a) {
return { "name": a.description, "step": a.step }
});
tree.forEach(function(v, i, arr) {
v.children = arr.filter(function(a) {
return a.step == v.step+1;
});
});
return tree.filter(function(a) {
return a.step == 1;
});
}
var formattedTaskflowTree = taskflowTree(taskflows[0].taskExecutions);
(function(treeData) {
// 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 < 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 && d.children.length > 0 ? d.children : null;
});
// sort the tree according to the node names
function sortTree() {
tree.sort(function(a, b) {
return b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1;
});
}
// Sort the tree initially incase the JSON isn't in a sorted order.
sortTree();
// TODO: Pan function, can be better implemented.
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 > 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);
// 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 && 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 && n.children.length > 0) {
if (levelWidth.length <= 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) * 25; // 25 pixels per line
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 * 10)); //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", 0)
.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('class', 'nodeText')
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
return d.name;
})
.style("fill-opacity", 0);
// 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", "red")
.attr('pointer-events', 'mouseover')
.on("mouseover", function(node) {
overCircle(node);
})
.on("mouseout", function(node) {
outCircle(node);
});
// 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 ? "lightsteelblue" : "#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);
// 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);
// 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);
})(formattedTaskflowTree[0]);
Posted is a screenshot of the re-occurring errors that I'm receiving upon loading up the tree.