Reputation: 16392
First need to say, that I'm totally new to d3.js (I'm using version 4). For now I have nested object of nodes and trying to draw child elements after clicking parent. And it works partly: I can draw child nodes, but not links from parent circle to child circles. I've got this errors:
Error: missing: mammal
and
TypeError: Cannot create property 'vx' on string 'mammal'
Looks like source
created successfully, but target
breaks at the first link and others:
I'm not sure where the issue is, so I have created a fiddle with all code that I have for now: https://jsfiddle.net/L6c6pxrv/4/
@KEKUATAN suggested me to assign new array to links
, don't push each item. The result is: https://jsfiddle.net/L6c6pxrv/3/. There is no errors anymore, but looks like links still doesn't know coordinates of source and target.
Here is what I'm trying to achieve after circle click (here I'm using solid array of nodes and links are defined from the very beginning): https://jsfiddle.net/L6c6pxrv/2/
Thanks for any help!
Upvotes: 0
Views: 619
Reputation: 958
just ask if you need explaination i will try if i can, but not now this time i must go home
like i said before each chart on d3 have structure to understand first before make it, uderstand this structure chart , if you see the data array on working findle you will see there is no children object and id or source betwen 2 array have same value. i assume your data dont have same data value betwen 2 data array, id and source, that is why you have
Error: missing: mammal
i add missing mamal to the children object
if you console the node and link, you will find this chart type generating vx and vy
TypeError: Cannot create property 'vx' on string 'mammal'
is a chain error cos there is no mammal so they cant make vx and vy on id = mammal
Bug:
i cant make "M" text on middle of node dancing like other.
Note:
d3.forceSimulation() and d3.forceLink() i think this is the core of creating chart like this and this is the reason they generated cildren or vx and vy
Tips:
make sure the links data and the nodes data have one same value like id and source so you will get the connection, try dont believe me this is just share what i thing i know, not what i know, try have better teacher to better understanding
var nodes = [
{
id: "mammal",
label: "Mammals",
fill: 'orange',
color: '#333333',
children: [
{
id: "mammal",
label: "Dogs",
fill: 'yellow',
color: 'orangered'
},
{
id: "dog",
label: "Dogs",
fill: 'yellow',
color: 'orangered'
},
{
id: "cat",
label: "Cats",
fill: 'white',
color: 'blue'
},
{
id: "fox",
label: "Foxes",
fill: 'green',
color: 'black'
},
{
id: "elk",
label: "Elk",
fill: 'black',
color: 'orangered'
},
{
id: "insect",
label: "Insects",
fill: '#333333',
color: 'lightblue',
children: [
{
id: "pike",
label: "Pikes",
fill: 'white',
color: 'forestgreen'
}
]
},
{
id: "ant",
label: "Ants",
fill: 'violet',
color: 'white'
},
{
id: "bee",
label: "Bees",
fill: 'white',
color: 'purple'
},
{
id: "fish",
label: "Fish",
fill: 'darkblue',
color: 'white'
},
{
id: "carp",
label: "Carp",
fill: 'purple',
color: 'white'
}
]
}
];
var links = [
// { target: "mammal", source: "dog" },
// { target: "mammal", source: "cat" },
// { target: "mammal", source: "fox" },
// { target: "mammal", source: "elk" },
// { target: "mammal", source: "insect" },
// { target: "mammal", source: "ant" },
// { target: "mammal", source: "bee" },
// { target: "mammal", source: "fish" },
// { target: "mammal", source: "carp" }
// { target: "insect", source: "pike" }
];
var width = window.innerWidth;
var height = window.innerHeight;
var svg = d3.select('svg');
// Append rect and make it draggable
svg.append('rect').style('width', width * 2).style('height', height * 2).style('fill', 'transparent');
var dragcontainer = d3.drag()
.on("drag", function(d) {
d3.select(this).attr("transform", "translate(" + (d.x = d3.event.x) + "," + (d.y = d3.event.y) + ")");
});
var g = d3.select('svg').select("g").datum({x: 0, y: 0}).call(dragcontainer);
var groupWrapper = svg.append('g');
// Zoom
var zoom = d3.zoom()
.scaleExtent([-Infinity, 100])
.on('zoom', zoomFn);
function zoomFn() {
d3.select('svg').select('g')
.attr('transform', 'translate(' + d3.event.transform.x + ',' + d3.event.transform.y + ') scale(' + d3.event.transform.k + ')');
}
d3.select('svg').call(zoom);
// simulation setup with all forces
var linkForce = d3
.forceLink()
.id(function (link) { return link.id })
.strength(function (link) { return 0.3 });
var simulation = d3
.forceSimulation()
.force('link', linkForce)
.force('charge', d3.forceManyBody().strength(-5000))
.force('center', d3.forceCenter(width / 2, height / 2));
var dragDrop = d3.drag().on('start', function (node) {
node.fx = node.x;
node.fy = node.y;
}).on('drag', function (node) {
simulation.alphaTarget(0.7).restart();
node.fx = d3.event.x;
node.fy = d3.event.y;
}).on('end', function (node) {
if (!d3.event.active) {
simulation.alphaTarget(0);
}
node.fx = null;
node.fy = null;
});
var linkElements = groupWrapper.append("g")
.attr("class", "links")
/**
* comment below
*/
.selectAll("line")
.data(links)
.enter().append("line")
.attr("stroke-width", 1)
.attr("stroke", "rgba(50, 50, 50, 0.2)");
var nodeElementsWrapper = groupWrapper.append("g").attr('class', '1');
var nodeElementsGroup = nodeElementsWrapper
.attr("class", "nodes")
.selectAll("circle")
.data(nodes)
.enter().append("g")
.attr('class', 'item')
.on("click", function(d) {
console.log("on click", d);
draw();
});
var circleElements = nodeElementsGroup
.append("circle")
.attr("r", 100)
.attr("fill", function(item) {
return item.fill;
})
.attr("stroke", function(item) {
return item.color;
})
.attr("stroke-width", function(item) {
})
.call(dragDrop);
var textElements = d3.selectAll('g.item')
.each(function(item, i) {
d3.select(this)
.append('text')
.attr("font-size", 50)
.attr("text-anchor", "middle")
.attr("alignment-baseline", "central")
.attr("fill", function () { return item.color })
.text(function () { return item.id[0].toUpperCase() })
});
// Draw children
function draw() {
var newLinks = [
{ target: "mammal", source: "dog" },
{ target: "mammal", source: "cat" },
{ target: "mammal", source: "fox" },
{ target: "mammal", source: "elk" },
{ target: "mammal", source: "insect" },
{ target: "mammal", source: "ant" },
{ target: "mammal", source: "bee" },
{ target: "mammal", source: "fish" },
{ target: "mammal", source: "carp" }
];
newLinks.forEach(function(item) {
links.push(item);
});
console.log('LINKS: ', links);
nodeElementsGroup = nodeElementsWrapper
.attr("class", "nodes")
.selectAll("circle")
.data(nodes[0].children)
.enter().append("g")
.attr('class', 'item')
.on("click", function(d) {
console.log("on click", d);
});
circleElements = nodeElementsGroup
.data(nodes[0].children)
.append("circle")
.attr("r", 100)
.attr("fill", function(item) {
return item.fill;
})
.attr("stroke", function(item) {
return item.color;
})
.attr("stroke-width", function(item) {
})
.call(dragDrop);
textElements = d3.selectAll('g.item')
.each(function(item, i) {
var t = d3.select(this).text()
if (t=='M'){
}else{
d3.select(this)
.append('text')
.attr("font-size", 50)
.attr("text-anchor", "middle")
.attr("alignment-baseline", "central")
.attr("fill", function () { return item.color })
.text(function () {
var s = item.id[0].toUpperCase()
if (s!=='M'){
return s
}
}).call(dragDrop)
}
})
linkElements = d3.select('g.links')
.attr("class", "links")
.selectAll("line")
.data(links)
.enter().append("line")
.attr("class", "link")
.attr("stroke-width", 1)
.attr("stroke", "rgba(50, 50, 50, 0.2)");
// simulation
simulation.nodes(nodes[0].children).on('tick', function() {
d3.selectAll('circle')
.attr('cx', function (node) { return node.x })
.attr('cy', function (node) { return node.y });
d3.selectAll('text')
.attr('x', function (node) { return node.x })
.attr('y', function (node) { return node.y });
d3.selectAll('line.link')
.attr('x1', function (link) { return link.source.x })
.attr('y1', function (link) { return link.source.y })
.attr('x2', function (link) { return link.target.x })
.attr('y2', function (link) { return link.target.y })
});
// simulation.force("link").links(d3.selectAll('line.link')); // "links" instead of d3.selectAll('line.link')
}
// Simulation
simulation.nodes(nodes).on('tick', function() {
circleElements
.attr('cx', function (node) { return node.x })
.attr('cy', function (node) { return node.y });
d3.selectAll('text')
.attr('x', function (node) { return node.x })
.attr('y', function (node) { return node.y });
linkElements
.attr('x1', function (link) { return link.source.x })
.attr('y1', function (link) { return link.source.y })
.attr('x2', function (link) { return link.target.x })
.attr('y2', function (link) { return link.target.y })
});
simulation.force("link").links(links);
html {
background-color: gray;
}
body {
position: relative;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
svg {
position: fixed;
top: 0;
width: auto;
height: auto;
min-width: 100%;
min-height: 100%;
background-color: gray;
}
circle {
cursor: pointer;
}
text {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
cursor: pointer;
}
line {
stroke: #fff;
stroke-width: 1.5;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.min.js"></script>
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<svg></svg>
</body>
</html>
Upvotes: 1