How to access attributes of an element inside a group? - javascript

I'm not sure if I've grouped my elements properly, but my layout in d3 is like so:
var circleGroup = svg.selectAll("g")
.data(nodeList)
.enter()
.append("g")
This creates a bunch a groups, I need a circle in each group:
circleGroup.append("circle")
.attr("cx", function(d,i){
return coordinates[i][0];
})
.attr("cy", function(d,i){
return coordinates[i][1];
})
.attr("r", function(d){
return 10;
})
.attr("fill", "white");
The data itself doesn't actually have any coordinate data so I dynamically arrange them in a circle and just position them based on index. I also add some labels. I repeat coordinates[i][0] here but is there a way to access the "cx" and "cy" attributes of the circles? I tried a few forms of d3.select(this) but I'm getting nothing.
circleGroup.append("text")
.attr("x", function(d,i){
return coordinates[i][0];
})
.attr("y", function(d,i){
return coordinates[i][1];
})
.style("text-anchor","middle")
.text(function(d,i){
return d;
});

Don't mess with indices, this is hard to maintain and error prone. Instead of that, given your specific tree structure, use node.previousSibling:
circleGroup.append("text")
.attr("x", function() {
return d3.select(this.previousSibling).attr("cx");
})
.attr("y", function() {
return d3.select(this.previousSibling).attr("cy");
})
Here is a demo using (most of) your code:
var svg = d3.select("svg")
var circleGroup = svg.selectAll("g")
.data(d3.range(5))
.enter()
.append("g");
circleGroup.append("circle")
.attr("cx", function(d, i) {
return 20 + Math.random() * 280;
})
.attr("cy", function(d, i) {
return 20 + Math.random() * 130;
})
.attr("r", function(d) {
return 10;
})
.style("opacity", 0.2);
circleGroup.append("text")
.attr("x", function() {
return d3.select(this.previousSibling).attr("cx");
})
.attr("y", function() {
return d3.select(this.previousSibling).attr("cy");
})
.style("text-anchor", "middle")
.text("Foo");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>

Related

Displaying text after an onclick event

I'm using click events to log data to the console, but i'd like to display this data in a separate box (which i have created). Does anyone have any advice or suggestions for this? Or is there a decent library that can help me achieve this?
Cheers
var circles = svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r", 7)
.attr("cx", function(d) { return xScale(d[1]); })
.attr("cy", function(d) { return yScale(d[2]); })
.on('click', function(d, i) {
console.log("click", d[0]);
})
.attr("fill", function(d) {
var result = null;
if (data.indexOf(d) >= 0) {
result = colours(d);
} else {
result = "white";
}
return result;
});
var textBox = svg.append("rect")
.attr("x", 5)
.attr("y", 385)
.attr("height", 150)
.attr("width", 509)
.style("stroke", bordercolor)
.style("fill", "none")
.style("stroke-width", border);
In the "click" listener just select your box, or use the selection you already have:
circles.on("click", function(d) {
selection.append("text")
//etc...
})
Here is a simple demo, click the circle:
var svg = d3.select("svg");
var circle = svg.append("circle")
.datum({
name: "foo"
})
.attr("cx", 100)
.attr("cy", 100)
.attr("r", 60)
.style("fill", "teal");
var box = svg.append("g")
.attr("transform", "translate(300,50)");
box.append("rect")
.attr("height", 50)
.attr("width", 100)
.style("stroke", "black")
.style("fill", "none")
.style("stroke-width", "2px");
circle.on("click", function(d) {
box.append("text")
.attr("x", 10)
.attr("y", 20)
.text(d.name)
})
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="300"></svg>
Finally, two tips: make your selection a group or any other valid container for the text, not a rectangle, because you cannot append a text to a rectangle. Also, be prepared for all kinds of problems trying to fit your texts inside that rectangle: wrapping texts in an SVG is notoriously complicated.

Can't append text to d3.js force layout nodes

OBJECTIVE: append text to each node in a d3 force layout
BUG: text is appended to the object (I think, see console) but not displayed on screen
Here's the jsfiddle.
node.append("svg:text")
.text(function (d) { return d.name; }) // note that this works for
// storing the name as the id, as seen by selecting that element by
// it's ID in the CSS (see red-stroked node)
.style("fill", "#555")
.style("font-family", "Arial")
.style("font-size", 12);
I'd be so grateful for any thoughts.
You can't add svg text to a svg circle. You should first create an svg g object (g stands for group) for each node, and than add a circle and a text for each g element, like in this code:
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("g");
var circle = node.append("circle")
.attr("class", "node")
.attr("id", function (d) { return d.name; })
.attr("r", 5)
.style("fill", function (d) {
return color(d.group);
});
var label = node.append("svg:text")
.text(function (d) { return d.name; })
.style("text-anchor", "middle")
.style("fill", "#555")
.style("font-family", "Arial")
.style("font-size", 12);
Of course, tick function should be updated accordingly: (also css a little bit)
circle.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
});
label.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y - 10;
});
Here is jsfiddle.

D3: Attach text to circle such that it has same priority as circle object

I am able to add text to my sketch, but I would like it if I could make my text attached directly to the circle. This means that if a circle gets over-written by another circle, the text will as well. On a higher level not, I am finding the d3 model hard for constructing objects in a way that makes them composable with different shapes, etc. The code seems very procedural to mean so any tips would be greatly appeciated :)
JSFiddle link
var link = "https://api.github.com/orgs/csci-4830-002-2014/repos"
d3.json(link, function(error, data) {
var w = 10000;
var h = 1000;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("line")
.data(data)
.enter()
.append("line")
.attr("x1", 5)
.attr("y1", 5)
.attr("x2", function (d,i){
return 30*d.forks_count;
})
.attr("y2", function (d,i){
return 30*d.open_issues_count;
})
.attr("stroke-width", 2)
.attr("stroke", "black");
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r", 40)
.attr("cx", function(d){ return 30*d.forks_count; })
.attr("cy", function(d){ return 30*d.open_issues_count; })
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("fill", "white")
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("dx", function(d,i){ return 30*d.forks_count; })
.attr("dy", function(d,i){ return 30*d.open_issues_count; })
.text(function(d){
if (d.name.indexOf("challenge") != -1)
return "C";
else
return "H";
});
});
With the way your code written right now, all the lines will be added first, then all the circles, and finally the texts. SVG will always put elements added later on top. So to achieve what you want, you will need to group them up. To do this, you will need to add a g element for each element of your data
var element = svg.selectAll(".element")
.data(data)
.enter()
.append("g")
.attr("class","element");
Now you can add the line, circle, and text to it
element.append("line")
.attr("x1", 5)
.attr("y1", 5)
.attr("x2", function (d, i) {
return 30 * d.forks_count;
})
.attr("y2", function (d, i) {
return 30 * d.open_issues_count;
})
.attr("stroke-width", 2)
.attr("stroke", "black");
element.append("circle")
.attr("r", 30)
.attr("cx", function (d) {
return 30 * d.forks_count;
})
.attr("cy", function (d) {
return 30 * d.open_issues_count;
})
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("fill", "white")
element
.append("text")
.attr("dx", function (d, i) {
return 30 * d.forks_count;
})
.attr("dy", function (d, i) {
return 30 * d.open_issues_count+6;
})
.style("text-anchor", "middle")
.text(function (d) {
if (d.name.indexOf("challenge") != -1) return "C";
else return "H";
});
You can check the updated JSFiddle at http://jsfiddle.net/9tp1yun7/2/

How do I get the index number from the array in d3?

In the following code how do I get the number for the index/row that is 'd'?
var docs = svg.selectAll("g")
.data(dataset)
.enter()
.append("g")
.attr("id", function(d) { return d["Identifier"]})
.attr("num", function(d) { return d}) // this
.attr("x", function(d) { return xScale(d3.time.format.iso.parse(d["UserDate"]))})
.attr("y", function(d) { return xScale(d3.time.format.iso.parse(d["UserDate"]))});
simply use :
function(d,i){ ... }
e.g.
.attr('id', function(d,i){return 'item'+i;})

How to display data value on every layout of stacked barchart in d3

I have created a stacked bar chart in d3.
Here I want to display the value like this below example (This is Simple Bar chart)
http://bl.ocks.org/enjalot/1218567
But not outside the bar, inside the bar like below :
http://bl.ocks.org/diethardsteiner/3287802
This is my Stacked function which is working fine :
function barStack(d)
{
var l = d[0].length
while (l--) {
var posBase = 0,
negBase = 0;
d.forEach(function (d) {
d = d[l]
d.size = Math.abs(d.y)
if (d.y < 0) {
d.y0 = negBase
negBase -= d.size
} else {
d.y0 = posBase = posBase + d.size
}
})
}
d.extent = d3.extent(d3.merge(d3.merge(d.map(function (e) {
return e.map(function (f) {
return [f.y0, f.y0 - f.size]
})
}))))
return d
}
For stacked Bar
svg.selectAll(".series")
.data(data)
.enter()
.append("g")
.attr("class", "g")
.style("fill", function (d, i) {return color(i)})
.selectAll("rect")
.data(Object)
.enter()
.append("rect")
.attr("x", function (d, i) {return x(x.domain()[i])})
.attr("y", function (d) { return y(d.y0) })
.attr("height", function (d) { return y(0) - y(d.size) })
.attr("width", x.rangeBand())
;
This was also running fine.
My data is like that
var data = [{x:"abc", y1:"3", y2:"4", y3:"10"},
{x:"abc2", y1:"6", y2:"-2", y3:"-3" },
{x:"abc3", y1:"-3", y2:"-9", y3:"4"}
]
Now I want to show this value of y1, y2 and y3 in every stacked layout.
I have tried this below code, but this is not displaying the value over layout.
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("transform", "translate(50,0)")
.attr("text-anchor", "middle")
.attr("x", function(d, i) {return (i * (width / data.length)) + ((width / data.length - 50) / 2);})
.attr("y", function(d) {return y(0) - y(d.size) + 14;})
.attr("class", "yAxis")
.text(function(d) {return y(d.size);})
;
Please help me on this, where I need to change or what exact I need to put instead of this or might be above code was totally wrong.
Please let me know if any more input required by me. I have the total POC and I can share that too.
i have added all code in jsfiddle
http://jsfiddle.net/goldenbutter/HZqkm/
Here is a fiddle that does what you want.
var plots = svg.selectAll(".series").data(data)
.enter()
.append("g")
.classed("series",true)
.style("fill", function(d,i) {return color(i)})
plots.selectAll("rect").data(Object)
.enter().append("rect")
.attr("x",function(d,i) { return x(x.domain()[i])})
.attr("y",function(d) { return y(d.y0)})
.attr("height",function(d) { return y(0)-y(d.size)})
.attr("width",x.rangeBand());
plots.selectAll("text.lab").data(Object)
.enter().append("text")
.attr('fill','black')
.attr("text-anchor", "middle")
.attr("x", function(d, i) { return x(x.domain()[i]) + (x.rangeBand()/2)})
.attr("y", function(d) {return y(d.y0) + 20})
.text(function(d) {return (d.size).toFixed(2);});

Categories