我正在创建一个类似于D3 Layout Tree的树,并试图绑定我的自定义JSON对象。切换树节点的代码如下:
function toggleAll(d) {
if (d.children) {
d.children.forEach(toggleAll);
toggle(d);
}
}
// Initialize the display to show a few nodes.
root.children.forEach(toggleAll);
toggle(root.children[1]);
// Toggle children.
function toggle(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
}
正如您所看到的,d
与children
属性紧密相关 - 我正在尝试重写此函数以接受我的JSON中的任何嵌套对象,以便它也可以切换/扩展。但是我还没有运气(尝试过hasOwnProperty)。有什么想法吗?
编辑:包括我的JSON结构:
{
"date": "10242013"
"id": 34534
"addr": "444 Little Lane"
"department": {id: 13, addr: "555 ShoeHorse Road", name: "CTS",…}
"manager": {id: 454, addr: "444 Little Lane", name: "Bobby Johnson",…}
...
忽略输入错误的JSON ^此对象可以在这些对象中包含许多嵌套对象和对象。我试图避免硬编码的解决方案,支持:接受所有"方法
答案 0 :(得分:1)
我已编写代码将您的自定义JSON转换为d3树布局所需的格式。
var json = {
"date": "10242013",
"id": "34534",
"addr": "444 Little Lane",
"department": {
"id": 13,
"addr": "555 ShoeHorse Road",
"name": "CTS",
"computer": {
"id": 56,
"name": "CT"
},
"electronics": {
"id": 65,
"name": "EC"
}
},
"manager": {
"id": 454,
"addr": "444 Little Lane",
"name": "Bobby Johnson"
}
};
function convertJSON(object) {
for (var key in object) {
if (typeof(object[key]) == "object") {
var obj = convertJSON(object[key]);
obj.key = obj.name ? obj.name : "";
obj.name = key;
if (!object["children"])
object["children"] = [];
object["children"].push(obj);
delete object[key];
}
}
return object;
}
json = convertJSON(json);
console.log(json);
var m = [20, 120, 20, 120],
w = 1280 - m[1] - m[3],
h = 800 - m[0] - m[2],
i = 0,
root;
var tree = d3.layout.tree()
.size([h, w]);
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
var vis = d3.select("#body").append("svg:svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
root = json;
root.x0 = h / 2;
root.y0 = 0;
function toggleAll(d) {
if (d.children) {
d.children.forEach(toggleAll);
toggle(d);
}
}
// Initialize the display to show a few nodes.
root.children.forEach(toggleAll);
update(root);
function update(source) {
var duration = d3.event && d3.event.altKey ? 5000 : 500;
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse();
// Normalize for fixed-depth.
nodes.forEach(function(d) {
d.y = d.depth * 180;
});
// Update the nodes…
var node = vis.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("svg:g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on("click", function(d) {
toggle(d);
update(d);
});
nodeEnter.append("svg:circle")
.attr("r", 1e-6)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeEnter.append("svg: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 = vis.selectAll("path.link")
.data(tree.links(nodes), function(d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("svg:path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
})
.transition()
.duration(duration)
.attr("d", diagonal);
// 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.
function toggle(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
}
.node circle {
cursor: pointer;
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font-size: 11px;
}
path.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="body">
<div id="footer">
d3.layout.tree
<div class="hint">click or option-click to expand or collapse</div>
</div>
</div>
其他可能的方法是覆盖treemap的children
函数,并迭代d
并使用tyepof
来查找它是否是孩子。但这并不容易。
在这种情况下,toggle
方法可能如下所示。
function toggle(d) {
for(var key in d){
if (typeof(d[key])=="object") {
if((/^_/).test(key)){
d[key.substr(0)] = d[key];
} else{
d["_"+key] = d[key];
}
delete d[key];
}
}
}