D3 js line overlaps node - d3.js

I am doing force layout, on event click i add new node and connect by line. But
line overlaps node. Why? My code is shown http://jsfiddle.net/WRGtL/
function click(d) {
if (d3.event.defaultPrevented) return; // ignore drag
//alert("clicked");
var d = {id:"d"};
nodes.push(nodeId[index]);
if(index==0)
links.push({source: a, target: nodeId[index]});
else
links.push({source: nodes[1], target: nodeId[index]});
index++;
start();
}

In SVG, the Z order of elements is the order they appear in the file. The easiest way of having all lines appear below the circles is to group all lines and all circles in their own groups and let these groups define the Z order.
I've simply added two groups linksG and nodesG that the elements will be created in:
var linkG = svg.append("g"),
nodeG = svg.append("g");
var node = nodeG.selectAll(".node"),
link = linkG.selectAll(".link");
see the JSFiddle for a demo.
Edit: forgot to save the fiddle. Link fixed.

Related

Adjusting small multiples sparklines

I have an heatmap that show some data and a sparkline for each line of the heatmap.
If the user click on a row label, then the data are ordered in decreasing order, so each rect is placed in the right position.
Viceversa, if the user click on a column label.
Each react is placed in the right way but I'm not able to place the sparkline.
Here the code.
When the user click on a row label, also the path inside the svg containing the sparkline should be updated.
And then, when the user click on a column label, the svg containing the sparkline should be placed in the correct line.
To place the svg in the right place, I try to use the x and y attributes of svg. They are updated but the svg doesn't change its position. Why?
Here is a piece of code related to that:
var t = svg.transition().duration(1000);
var values = [];
var sorted;
sorted = d3.range(numRegions).sort(function(a, b) {
if(sortOrder) {
return values[b] - values[a];
}
else {
return values[a] - values[b];
}
});
t.selectAll('.rowLabel')
.attr('y', function(d, k) {
return sorted.indexOf(k) * cellSize;
});
Also, I don't know how to change the path of every sparkline svg. I could take the data and order them manually, but this is only good for the row on which the user has clicked and not for all the others.
How can I do?
The vertical and horizontal re-positioning/redrawing of those sparklines require different approaches:
Vertical adjustment
For this solution I'm using selection.sort, which:
Returns a new selection that contains a copy of each group in this selection sorted according to the compare function. After sorting, re-inserts elements to match the resulting order.
So, first, we set our selection:
var sortedSVG = d3.selectAll(".data-svg")
Then, since selection.sort deals with data, we bind the datum, which is the index of the SVG regarding your sorted array:
.datum(function(d){
return sorted.indexOf(+this.dataset.r)
})
Finally, we compare them in ascending order:
.sort(function(a,b){
return d3.ascending(a,b)
});
Have in mind that the change is immediate, not a slow and nice transition. This is because the elements are re-positioned in the DOM, and the new structure is painted immediately. For having a slow transition, you'll have to deal with HTML and CSS inside the container div (which may be worth a new specific question).
Horizontal adjustment
The issue here is getting all the relevant data from the selection:
var sel = d3.selectAll('rect[data-r=\'' + k + '\']')
.each(function() {
arr.push({value:+d3.select(this).attr('data-value'),
pos: +d3.select(this).attr('data-c')});
});
And sorting it according to data-c. After that, we map the result to a simple array:
var result = arr.sort(function(a,b){
return sorted.indexOf(a.pos) - sorted.indexOf(b.pos)
}).map(function(d){
return d.value
});
Conclusion
Here is the updated Plunker: http://next.plnkr.co/edit/85fIXWxmX0l42cHx or http://plnkr.co/edit/85fIXWxmX0l42cHx
PS: You'll need to re-position the circles as well.

How do I "register" a dragged group to specific coordinates with d3?

I'm trying to do something that (it seems to me!) should be simple,
but my attempts are getting very convoluted, and I'm looking for d3
guidance.
Suppose I have a dragged group object (consisting of a rectangle and
its text) that has been dropped someplace at the end of a drag. I want
to "register" this group at specific coordinates. How do I do that?
I am adding my code to the dragended() function associated with
d3.drag's on("end") event.
function dragended(d) {
var move = d3.select(this);
var g = move._groups[0][0]; // same as this!
var rect = g.children[0]
rect.x = schedLeft;
rect.y = schedTop;
d3.select(this).classed("active", false);
}
I bind d3.select(this) to the variable move, and get an object
like that shown in the attached figure (Chrome developer Local).
EDIT: move._groups[0][0] is silly; it's the same as this!
Using this I can get the group (with child rect and
text nodes) that I want to move.
schedLeft is the x coordinate where I want the group dropped. The rect node has x and y attributes, but my rect.x = schedLeft
doesn't change anything (watching in the debugger).
Is that even the right way to have a transition of the entire group
(ie, including the attending text) to its new location?
Thanks to hints I found on SO here (from 2013!) I got it working using this:
function dragended(d) {
// register dragged move into hourSched
var top = schedTop + (nsched++) * menuWOHgt;
var transX = newDropX - d.x ;
var transY = newDropY - d.y;
var tstr = `translate( ${transX}, ${transY} )`;
d3.select(this).attr("transform", tstr)
.classed("active", false);
}

Draw/update circles in rect - nested selections confusion

I am trying to get a layout that would, for each object in the data array:
append a rect or a g element that will serve as container
inside or on top of this, append a circle for each of the coordinates.
Below is a mock-up of how the data is massaged before I'm trying to append to the DOM (at the top of the update() function in the block below):
[{
label: 'foo',
circles: [
{ x: 0, y: 10 },
{ x: 10, y: 10 }
]
},{
...
}]
The drawing and updating of the rect elements seems to be working fine, but I am getting the selection and joins confused for the circles.
Here's the block: http://blockbuilder.org/basilesimon/91f75ab5209a62981f11d30a81f618b5
With
var dots = rects.selectAll('.dots')
I can select the right data below but can't draw it.
Could you help me getting the selections right so I can draw and update both the rect and the circle, please?
Thank you Gerard for your help. This is my current state, but I've pitted myself into a hole by running a for loop instead of d3 selections.
I wonder if I couldn't nest the circles in g elements after building a new data object like so:
var data = dataset.map(function(d) {
var circles = d3.range(d.amount).map(function(i) {
return {
x: (i % 5)*20,
y: (i / 5 >> 0)*20
}
});
return {
label: d.label,
dots: circles
};
});
From each object in data, we'll append a g, and inside each g we'll append the circles. Any help appreciated, since this will affect the dots + i used by the update pattern...
New question here
Here is the problem:
var dots = svg.selectAll('dots')
You're selecting something that doesn't exist. Because of that, your "enter" selection will always contain all the data, and your "exit" selection will always be empty.
The solution is changing it for something like this:
var dots = svg.selectAll(".dots" + i)
And, in the enter selection, setting the classes:
.attr("class", "dots" + i)
Here is your updated bl.ocks (with some other minor changes): https://bl.ocks.org/anonymous/4c2e1d66f1ab890da983465a4f84ca9b

How to find links in d3 v4?

I have used the following code in d3 v3 to find nodes and links for a horizontal tree (top to bottom). Here is how the tree will look like:
var nodes = d3.layout.tree().nodes(jsonData).reverse();
var links = d3.layout.tree().links(nodes);
I'm trying to do the same in d3 v4. The v4 is missing the tree.links(nodes) method where it can accept the nodes.
How can I find the links in d3 v4?
I'm just adding this to save myself (and possibly others) time from having to dig the answers out of the demos. I'm new to D3 as well so I'm hoping this is as helpful to others as it was to me.
Use d3.hierarchy() & node.descendants() to get nodes and links.
// Assigns parent, children, height, depth, etc..
var root = d3.hierarchy(jsonData);
// Assigns the x and y coordinates for the nodes.
tree(root);
// Returns array of node objects.
var nodes = root.descendants();
// Returns array of link objects between nodes.
var links = root.descendants().slice(1);
//var links = root.links(); // to get objects with source and target properties.
Can also shorten this down a bit if wanted which still does all of the above.
var root = d3.hierarchy(jsonData),
nodes = tree(root).descendants(),
links = nodes.slice(1);
If you need to grab nodes/links within an event. For example on drag start of a node when using a tree diagram (and I'm sure this could be useful elsewhere as well).
var drag = d3.drag()
.on("start", function dragStart(d) {
var nodes = d.descendants(),
links = nodes.slice(1);
});
Or alternatively use node.links() where each link defines source and target properties.
var drag = d3.drag()
.on("start", function dragStart(d) {
var nodes = d.descendants(),
links = d.links();
});
You would think this would also work in this case but it doesn't (bummer).
var root = d3.hierarchy(jsonData),
nodes = tree(root).descendants(),
links = nodes.links(); // <-- only works on a single node, resort to slice(1).

select only updating elements with d3.js

With a d3.js join Is there a way to select only the 'updating' elements separately from the 'entering' elements?
updateAndEnter = d3.selectAll('element').data(data);
entering = updateAndEnter.enter();
exiting = updateAndEnter.exit();
updatingOnly = ??;
Yes, the selection just after the data join contains the 'update only' elements. After appending to the enter() selection, it will be expanded to include the entering elements as well.
See General Update Pattern:
// DATA JOIN
// Join new data with old elements, if any.
var text = svg.selectAll("text")
.data(data);
// UPDATE
// Update old elements as needed.
text.attr("class", "update");
// ENTER
// Create new elements as needed.
text.enter().append("text")
.attr("class", "enter")
.attr("x", function(d, i) { return i * 32; })
.attr("dy", ".35em");
// ENTER + UPDATE
// Appending to the enter selection expands the update selection to include
// entering elements; so, operations on the update selection after appending to
// the enter selection will apply to both entering and updating nodes.
text.text(function(d) { return d; });
// EXIT
// Remove old elements as needed.
text.exit().remove();
it's my pleasure
For me ( too ) this is a little bit confusing : it seems that the only available set is actually ENTER+UPDATE ( blended together ) and EXIT.
But what if i want to work or at least identify only updated elements?
I wrote a very simple function ( that follows, simply put wrap it in a script tag at the end of a basic html page ) showing this simple dilemma : how do I highlight updated elements ? Only ENTER and EXIT seem to react "correctly"
To test it, just type in chrome console :
manage_p(['append','a few','paragraph'])
manage_p(['append','a few','new','paragraph'])
manage_p(['append','paragraphs'])
I can get green or red highlighting, i can't get white
Maybe we're missing D3Js specs?
Best regards,
Fabrizio
function join_p(dataSet) {
var el = d3.select('body');
var join = el
.selectAll('p')
.data(dataSet);
join.enter().append('p').style('background-color','white');
join.style('background-color','green').text(function(d) { return d; });
join.exit().text('eliminato').style('background-color','red');
}

Resources