I am trying to get a subselection of a given D3.js selection.
This part of the code creates the paths:
pieces.paths = pieces.groups
.append("path")
.attr("fill", function (d) { return d.data.color; });
Then, I set de "d" attribute:
pieces.paths
.attr("d", arc);
Working perfect. But pieces.paths has 3 elements, and I want to set the class of the first two elements to "highest". How may I do that?
selection.filter() is one option:
pieces.paths
.filter(function(d, i) {return i<2;})
.attr("class", "highest");
Related
I have two d3 visualizations, and when I click on a link on the sankey diagram (defined by an email), I want to change the radius of the corresponding node (with the same email) on the force directed network graph. No matter what I try or search on the internet I cannot find a way to adjust the attribute of one individual node without selecting all the circles.
var link = svg.append("g").selectAll(".link")
.data(graph.links)
.enter().append("path")
.attr("class", "link")
.on("click", function (d,i){
svg2.selectAll("circle").each(function (f,i){
if (f.id == d.email)
{
//CHANGE RADIUS OF F or color or anything
//svg2.select(f).attr("r",10); DOESNT WORK
//f.attr("r",10) DOESNT WORK
}
});
})
.attr("d", path)
.style("stroke-width", function(d)
{ return Math.max(0.2, d.dy); })
.sort(function(a, b) { return b.dy - a.dy; });
I have a d3.js line chart which may have negative values. My Y-span starts at 0 and goes up to the maximum value in the dataset. Thus, the line goes underneath the X-axis when there is a negative value.
This is fine, but what I need is to just hide the part of the line that is underneath the X-axis. I want to keep the values as they are, just use some CSS or JS to make the part of the line underneath the X-axis invisible.
I have tried with various overflow settings, but it doesn't seem to help. It is possible to make everything below the X-axis invisible by placing an element over that part, but then the X-axis labels are hidden too.
This is the code drawing the line:
x.domain([d3.min(data, function(d) { return d.date}), d3.max(data, function(d) { return d.date})]);
y.domain([0, 1.05 * d3.max(data, function (d) { return d.value; })]);
area.y0(y(0));
g.append("path")
.datum(data)
.attr("fill", "#f6f6f6")
.attr("d", area);
//create line
var valueline = d3.line()
.x(function (d) { return x(d.date); })
.y(function (d) { return y(d.value); });
g.append("path")
.data([data])
.attr('fill', 'none')
.attr('stroke', '#068d46')
.attr("class", "line")
.attr("d", valueline);
Fiddle: https://jsfiddle.net/c1bvrd50/1/
One way to solve this problem is to clip the line inside a rectangle covering the positive values area. In SVG, this is done with clip-path.
This happens in two steps:
Define a rect inside clipPath, covering the chart area only:
g.append('clipPath')
.attr('id', 'clipRect')
.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', width)
.attr('height', height)
Clip the line path, using clip-path attribute:
g.append("path")
.data([data])
/* ... */
.attr("d", valueline)
.attr('clip-path', 'url("#clipRect")')
Updated jsFiddle implementing the solution: here.
What is left to do is update the tooltip's mousemove event listener in order not to display the tooltip whenever the value is lower than 0.
Recommendation as P.S.: Whatever the use case of the chart is, it is most likely desirable to show the portions of the chart with negative values. Therefore, the chart's y axis should cover values lower than 0, rather than omitting these.
I have a filter function which fades out the rest of the nodes and highlights the selected set of nodes. I also want the labels associated to the nodes to reflect the same, i.e. fade out for the rest and only show the ones that are associated to the selected nodes. I have the following snippets:
// define properties of nodes
var node = g.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "dataNodes")
.attr('id', function(d){ return 'id' + d.id; });
// define visual properties of node labels
var text = g.append("g")
.attr("class", "labels")
.selectAll("text")
.data(graph.nodes)
.enter().append("text")
.attr("font-size","6px")
.attr("dx", 6)
.attr('id', function(d){ return 'id' + d.id; })
.attr("dy", ".15em")
.text(function(d) {
return d.name;
});
function filterByName(relevantArray){
d3.selectAll(".dataNodes").transition().duration(toggleTime).style("opacity", 0.3);
d3.selectAll(".labels").transition().duration(toggleTime).style("opacity", 0.3);
d3.selectAll(relevantArray).transition().duration(toggleTime).style("opacity", 1);
}
The filter function is passed with a set of IDs which is used for setting the opacity of the nodes and labels. Since the ID of both the node and associated label is the same, why is only the node's properties getting affected by the function and not the label?
You just answered your own question here:
Since the ID of both the node and associated label is the same,...
IDs are unique. You cannot have two elements with the same ID.
Thus, set different IDs for the texts...
.attr('id', function(d){ return 'idCircle' + d.id; })
And for the circles...
.attr('id', function(d){ return 'idLabel' + d.id; })
However, in your case, to select both the circle and the text later, you can simply use class instead:
.attr('class', function(d){ return 'class' + d.id; })
Because, unlike IDs, you can have two elements with the same class and, therefore, you can use selectAll to select all of them:
d3.selectAll(".someClass")
.transition()
//etc
I'm working on building a molecule creator in D3 using the original example created by Mike Bostock: http://bl.ocks.org/mbostock/3037015
I'm using a force layout just like in the example. My problem is when I add a new Node/atom dynamically, it doesn't move with the rest of the graph. I've read other questions and implemented everything that is suggested and have made sure that I'm following the proper update/join procedure that D3 requires and yet still the added carbon refuses to move with the rest of the graph.
Here is my update/create function:
function buildMolecule () {
// Update link data
link = link.data(links);
// Create new links
link.enter().append("g")
.attr("class", "link")
.each(function(d) {
d3.select(this)
.insert("line", ".node")
.style("stroke-width", function(d) { return (d.bond * 2 - 1) * 2 + "px"; });
d3.select(this)
.filter(function(d) { return d.bond > 1; }).append("line")
.attr("class", "separator");
d3.select(this)
.on("click", bondClicked);
});
// Delete removed links
link.exit().remove();
// Update node data
node = node.data(nodes);
// Create new nodes
node.enter().append("g")
.attr("class", "node")
.on("click", atomClicked)
.each(function(d) {
console.log('d:', d);
// Add node circle
d3.select(this)
.append("circle")
.attr("r", function(d) { return radius(d.size); })
.style("fill", function(d) { return color(d.atom); });
// Add atom symbol
d3.select(this)
.append("text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.atom; });
d3.select(this).call(force.drag);
});
// Delete removed nodes
node.exit().remove();
force.start();
}
JsFiddle: http://jsfiddle.net/2dPMF/1/
Any help would be greatly appreciated!
You're modifying the data structures of the nodes and links beyond adding and deleting nodes, which messes up the force layout. I'm talking about these lines in particular.
function bigBang () {
links = links.concat(linksList);
nodes = nodes.concat(nodesList);
buildMolecule();
}
The first two lines in that function are what I'm talking about. Working jsfiddle here.
In Mike Bostocks example http://bost.ocks.org/mike/nations/ there is so much data that putting the names of the countries there would make it chaotic, but for a smaller project I would like to display it.
I found this in the source:
var dot = svg.append("g")
.attr("class", "dots")
.selectAll(".dot")
.data(interpolateData(2004))
.enter().append("circle")
.attr("class", "dot")
.style("fill", function(d) { return color(d); })
.text(function(d) { return d.name; })
.call(position)
.sort(order);
dot.append("title")
.text(function(d) { return d.name; });
But somehow a title never shows up. Does anybody have an idea, how to display the name, next to the bubble?
As the other answer suggests, you need to group your elements together. In addition, you need to append a text element -- the title element only displays as a tooltip in SVG. The code you're looking for would look something like this.
var dot = svg.append("g")
.attr("class", "dots")
.selectAll(".dot")
.data(interpolateData(2004))
.enter()
.append("g")
.attr("class", "dot")
.call(position)
.sort(order);
dot.append("circle")
.style("fill", function(d) { return color(d); });
dot.append("text")
.attr("y", 10)
.text(function(d) { return d.name; });
In the call to position, you would need to set the transform attribute. You may have to adjust the coordinates of the text element.
Unfortunately grouping the text and circles together will not help in this case. The bubbles are moved by changing their position attributes (cx and cy), but elements do not have x and y positions to move. They can only be moved with a transform-translate. See: https://www.dashingd3js.com/svg-group-element-and-d3js
Your options here are:
1) rewrite the position function to calculate the position difference (change in x and change in y) between the elements current position and its new position and apply that to the . THIS WOULD BE VERY DIFFICULT.
or 2) Write a parallel set of instructions to setup and move the tags. Something like:
var tag = svg.append("g")
.attr("class", "tag")
.selectAll(".tag")
.data(interpolateData(2004))
.enter().append("text")
.attr("class", "tag")
.attr("text-anchor", "left")
.style("fill", function(d) { return color(d); })
.text(function(d) { return d.name; })
.call(tagposition)
.sort(order);
You will need a separate tagposition function since text needs 'x' and 'y' instead of 'cx', 'cy', and 'r' attributes. Don't forget to update the "displayYear" function to change the tag positions as well. You will probably want to offset the text from the bubbles, but making sure the text does not overlap is a much more complicated problem: http://bl.ocks.org/thudfactor/6688739
PS- I called them tags since 'label' already means something in that example.
you have to wrap the circle element and text together , it should look like
<country>
<circle ></circle>
<text></text>
</country>