In D3, how do you conditionally nest SVG elements (in the context of 'enter')? - d3.js

Specifically, given this example: https://bl.ocks.org/mbostock/4062045
How would I nest circles inside of the others? And have those be able to link to other (parent) circles?

The key for me was to use what was returned from 'enter' for appending the children and parent SVG objects separately. e.g.:
var enter = svg...enter()
var node = enter.append("rect")...
var children = enter.append("rect")...
(see the link in the question for the entire code context)
To conditionally add children, you can use a filter as follows:
var children = enter.filter(...).append("rect")...

Related

removing all svg elements in D3 save some specified

const svg=d3.append("svg").attr(...)
const g1=svg.append("g").attr("id","g1").attr(...)
var v1=g1.append(...)
... // many other nested append operations
const g2=g1.append("g").attr("id","g2").attr(...)
var v2=g2.append(...)
... // many other nested append operations
Then how can I remove all elements from svg object apart groups g1 and g2?
Something similar to:
svg.selectAll("*:not('#g1'):not('#g2')").remove();
that does not work.
I would apply a class to all elements that you want to keep, e.g.:
const svg=d3.append("svg")
.attr(...)
const g1=svg.append("g")
.attr(...)
.classed('doKeep', true)
var v1=g1.append(...)
// After adding many nested elements to g1
g1.selectAll('*')
.classed('doKeep', true)
...
const g2=g1.append("g")
.attr(...)
.classed('doKeep', true)
var v2=g2.append(...)
...
// After adding many nested elements to g1
g2.selectAll('*')
.classed('doKeep', true)
Then you can select all elements in you SVG that do not have that class, with a :not() CSS selector as such:
svg.selectAll(':not(.doKeep)').remove()
The inverse would also work, where you mark all elements you want to delete, and then delete them after selecting them. But giving the phrasing of your question, this is the most accurate approach.
Hope this helps!
Edit: Updated to reflect updated question that specifies many nested elements.

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).

how to get silbling elements in sunburst

I am new to d3.js, I am creating a visualization based on surburst. Can somebody let me know how I can get siblings elements external element in d3.js.
The main part of building the hierarchy from your data will be done by d3. Check the spec for partition.nodes(root) on how this information is put into the nodes. Basically, d3 will populate each node with a reference to its parent and its children providing all that is needed to navigate the hierarchy.
As a starting point you may have a look at this sunburst diagram. When hovering over an arc, this arc as well as its ancestors up to the root node are highlighted. The selection of nodes up the hierarchy which are to be highlighted takes place in a single function:
// Given a node in a partition layout, return an array of all of its ancestor
// nodes, highest first, but excluding the root.
function getAncestors(node) {
var path = [];
var current = node;
while (current.parent) {
path.unshift(current);
current = current.parent;
}
return path;
}
An even simpler version of this function will select the node and its siblings instead of the ancestors:
function getAncestors(node) {
return node.parent.children;
}
I adapted the above example to a plunk demonstrating how this may help solve your problem. In this plunk the hovered node and its siblings will get highlighted.

.append() to different selection depending on condition?

I have a similar issue as in Updating SVG Element Z-Index With D3
My preferred solution would be to have two groups within my svg as described in the answer by notan3xit.
But I have one data set, and a boolean flag on one of the data properties determines to which group the svg element belongs.
svg.selectAll("path")
.data(d)
.enter()
.if(d.property).appendToGroup1()
.else.appendToGroup2()
This obviously doesn't work, but something like this.
I only want to iterate through the data once, and during runtime append the generated svg elements to the according groups.
Any ideas how to go about achieving that with d3.js?
Try
.each(function(d){
var toAppend = $(this);
if (!!d.property){
toAppend.appendtoGroup1();
} else {
toAppend.appendToGroup2();
}
})//...
The general idea would be to pass d into a callback, and apply the right append method inside that callback.
It should be cleaner to first save reference to what you want to append to, in an enclosing scope so you can easily call .append() on it.
Try this:
var selectedPath=svg.selectAll("path").data(d);
svg.selectAll("path").each(function(d,i){
if(d3.select(this).attr("property's name"))
selectedPath.append("some data to group 1")
else
selectedPath.append("some data to group 2")
});
If I had your code better able to help.

How data in a d3 scatterplot could be referenced/called outside d3 selection

I have a d3 scatterplot and want to know how to get the value of the data appended to one single record (circle) in order to call it later in the code.
Say we have the following data:
ID X Y
A 1 1
B 2 2
C 3 3
How can I get the X value of the record B?
Does anyone have an idea how to do it?
thx
You may need to show more code, as it depends on how you're attaching the data. But assuming that you have something like
var circle = svg.selectAll("circle").data(myArrayOfDataObjects)
.enter().append("circle");
You can assign an ID or class to the DOM element you create:
circle.attr("id", function(d) { return "dataRow" + d.ID; });
Now you can use d3, plain JS, or the library of your choice to get a handle on the DOM element later on. D3 attaches its data as a __data__ property on the DOM element, so you can reference that to get your datum. D3 also provides the .datum() method to get this value:
var myDataRow = d3.selectAll("#dataRowB").datum();
var xValue = myDataRow.X;
Or, in plain JavaScript:
var myDataRow = document.getElementById("#dataRowB").__data__;
// etc
If jQuery is an option, in your d3 you can use the jQuery .data() function to attach the d3 data directly to the SVG element. Simply run something like .each(function(d){ $(this).data(d) }).
You can then access the data anywhere by using $(selector).data(), where selector points to the DOM element or elements that you're interested in, with something like '#RecordB'.
If your data is more complex, (say, d = {foo: 'foo', bar: 'bar'}), you'd use $(selector).data()[foo] to grab whatever data is keyed to 'foo' in the data associated with whatever is pointed at by the selector.

Resources