I am using D3 v4 to make a dynamic social network visualization and an example is here: https://jsfiddle.net/wand5r6L/1/.
There are two years of data in this example, and I want to update the nodes and links when 2004 comes to 2005. I want to make the positions of nodes and links to dynamically change as new nodes and links are added, but they just pop out at once regardless of the original position of old nodes and links.
I think I should add transition() to the tick() function but it does not work and causes more bugs.
Any suggestions? Thanks in advance.
This is the modifying a force layout block example from Mike Bostock:
https://bl.ocks.org/mbostock/0adcc447925ffae87975a3a81628a196
This is an updated jsfiddle I forked from the one you made. Mostly I specified a key function in the data links:
https://jsfiddle.net/eonny83k/1/
var node = g2
.attr("class", "nodes")
.selectAll("circle")
.data(nodeData, function(d) { return d.source + '-' + d.target });
Related
I want to display multiple force directed graphs. Preferably with only using 1 svg element as well (as I think that increases performance as well as let me make a width and height for whole simulation as this may differ based on data).
My first thought and with some research I just did a for loop, re-evaluated my nodes/edges arrays and put that in a function that generates the force-directed graph.
for (var i = 0; i < sampleData.length; i++)
{
var nodes = [];
var edges = [];
x = x+100; //update position (i want to show grpahs side by side.)
root = sampleData[i];
nodes.push({"name": root, "x": x, "y": y, "fixed": true, "color": "purple"});
//These 2 recurisve functions generate my nodes and edges array just fine.
buildParents(childs, parents, test, counter);
buildChildren(childs, parents, test, counter);
//apply id to each node
nodes.forEach((d,i)=>d.generatedId='id'+i);
//build makes the force-directed layout.
build(nodes, edges);
}
This actually appears to work fine for me with my nodes and links. My issue is the text does not display for all nodes like it does if I only pass in one set of data. I have text defined as so in the force simulation:
var nodes_text = svg.selectAll(".nodetext")
.data(nodes)
.enter()
.append("text")
.attr("class", "nodetext slds-text-heading--label")
.attr("text-anchor", "middle")
.attr("dx", -20)
.attr("dy", 20)
.text(d=>d.name)
.attr('opacity',0)
I was able to reproduce this error by just making 2 arrays for nodes and 2 arrays for edges and passing it into a simulation. Here is a simple program that reproduces my error:
https://jsfiddle.net/mg8b46aj/9/
I think fixing this JFiddle would give me the right idea on how to put it in my program.
So I just call the build function twice (either order is error). The left node has text but one of them isn't the correct text field. Also dragging it around a little bit makes it "leave" its text behind. The right graph has nothing.
Edit: And clicking the a node on the right graph seems to reset the text positions.
The problem is with the d3 selector use for selecting labels. As you need two separate force layout diagrams, you should use a selector as shown below for labels.
var nodes_text = svg.append('g') //Append new group for labels in new diagram
.attr("class", "labels")
.selectAll(".nodetext")
.data(nodes)
.enter()
.append("text");
Updated Fiddle: https://jsfiddle.net/gilsha/qe7bbnwn/1/
Currently, I am working on clubbing together all the child nodes together in a rectangle rather than creating a seperate parent circle for future reference. Following is an example of the sankey chart i am working on:
bi-directional sankey chart by Neilos.
However i am not able to understand the logic used for collapsers here. For example:
collapser = svg.select("#collapsers").selectAll(".collapser")
.data(biHiSankey.expandedNodes(), function (d) { return d.id; });
This will create collapser to which you will append the circle. Now, is there any way we can append all the child nodes to this circle(by making it big enough), so that graphically we can show that all the child nodes belong to same parent class?
At line 433 of the default sankey code you see:
collapserEnter.append("circle")
.attr("r", 100)
.style("fill", function (d) {
d.color = colorScale(d.type.replace(/ .*/, ""));
return d.color;
});
Does this mean than i can try appending the child nodes to this collapserEnter object? If not, is there any other way we can do it?
All help will be appreciated.
I realise this question has been asked before but I can't get to the bottom of it.
Here is my chart... http://www.gogeye.com/financialnews/piechart/index3.html
All I want to do is have the coin render behind the graph. I know D3 renders in order they are appended.
I have tried to re-append the coin but can't seem to get it working.
I've tried reordering when things are appended in the DOM but keep getting errors probably because variables are getting called before being defined etc.
Can someone give me an example of how to fix this with my code? I don't want you to do the work for me but I've been pulling my hair out for so long, I can't seem to apply other peoples examples to mine.
thanks
I would recommend creating some "layers" using svg g elements which stands for "group".
When you render your chart, you can first define your layers:
var layer1 = svg.append('g');
var layer2 = svg.append('g');
var layer3 = svg.append('g');
// etc... for however many layers you need
Then when you append new elements, you can decide which layer you want them to be on, and it won't matter what order you assign them in, because the group elements have already been added to the DOM, and are ordered. For example:
var layer1 = svg.append('g');
var layer2 = svg.append('g');
var redCircle = layer2.append('circle')
.attr('cx', 50)
.attr('cy', 50)
.attr('r', 16)
.attr('fill', 'red')
var blueSquare = layer1.append('rect')
.attr('x', 25)
.attr('y', 25)
.attr('width', 50)
.attr('height', 50)
.attr('fill', 'blue');
In this case the red circle will be visible above the blue square even though the blue square was created last. This is because the circle and the square are children of different group elements, which are in a pre-defined order.
Here's a FIDDLE of the above example so you can see it in action.
Doing this should take a lot of the guesswork out of when to add certain elements to your chart, and it also helps to organize your elements into a more logical arrangement. Hope that helps, and good luck.
I am using the D3.js, and found that it has a built-in function for changing the z-order of SVG elements programmatically after the original drawing.
RipTutorial: svg--the-drawing-order covers the d3 builtin function
Quotes from this link:
selection.raise(): Re-inserts each selected element, in order, as the last child of its parent. selection.lower(): Re-inserts each selected element, in order, as the first child of its parent.
d3.selectAll("circle").on("mouseenter", function(){
d3.select(this).raise();
});
d3.selectAll("circle").on("mouseleave", function(){
d3.select(this).lower();
});
see live example their jsFiddle
I'm working with this jsfiddle. I expect that when I click the WeekView button it should change the bar colors to reflect the same colors that are in my legend. But for some reason the colors are different.
I don't think
var layer = svg.selectAll(".layer")
.data(stack);
layer.enter()
.append("g")
.attr("class", "layer")
.style("fill", function (d, i) {
return color(i);
});
layer.exit()
.remove();
is being called when I switch to weekview, therefore its not replacing the old bars with the new ones its just reusing the bars from the previous views.
How can I get d3.js to replace the bars with the proper colors?
Indeed, the problem is in that part of the code: Demo
var layer = svg.selectAll(".layer")
.data(stack);
layer.enter()
.append("g")
.attr("class", "layer");
// Set the colors in the `update` cycle, not the `enter` cycle.
layer.style("fill", function (d, i) {
return color(i);
});
layer.exit()
.remove();
There is an interesting history of why this behaves this way. In earlier versions of D3, the enter and update set of elements were kept separate, just like update and exit events are still kept separate, i.e. operations you performed on the update set would not be performed on the exit set and vice-versa.
However, in version 2.0 of D3, it was decided that any element appended in the enter phase would also become a part of the update set. This was done because often the enter set of elements and the update set of elements needed to have the exact same operation performed on them (like in your case). To avoid this effect, you'll need to write the update phase before the enter phase.
Hence, in the enter cycle, elements should be appended and their initial attributes should should be set while their final values (which they should have in static state) should be set in the update cycle.
How do I implement tooltips on mouse over for links in a D3 directed graph layout? I'm adapting the D3 force example, so setting up node tooltips was straightforward using code like this:
node.append("title")
.text(function(n) {
return n.id;
});
Trying a similar technique with the links didn't result in mouse over tool tips:
var link = svg.selectAll("line.link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return 4;
});
link.append("title")
.text(function(n) {
return n.info;
});
You can find different solutions suggested by Mike Bostock on this Google Groups thread "show value when click or move mouse over on d3.svg.line"
I think what you are looking for is a combination of these two answers:
d3js: _on()_ doesn't send the current datum object to the onmouse function
and
Adding tooltip to bar chart generated using svg path
Both have jsFiddles you can play with.
Setting the link title as shown above does result in mouse over tooltips -- iff you let the mouse hover over any portion of the link a couple of seconds.