D3 append text to circles on a line not working properly - javascript

I created some line charts on d3. After that I appended some circles to every 3rd element in the line. Now I want to append text to those circles.
// created a circle variable
var circles=g2
.append("g")
.attr("id","symbols-b")
.selectAll("circles")
.data(slicesCircle)
.enter()
.append("g")
circles.style("fill", function(d){
return d.color=color(d.id);
})
//append circles
circles
.selectAll("circle")
.data(function (d){return d.values})
.enter()
.filter((d,i)=>(i%3==0) && i>0) //to attach every 3rd datapoint
.append("circle")
.attr("r", 7.5)
.attr("cx", function(d,i) {return xScale(d.date);})
.attr("cy", function(d,i) {return yScale(d.measurement);})
// Using the circle variable again to append text
circles
.selectAll("circle")
.data(function (d){return d.values})
.enter()
.filter((d,i)=>(i%3==0) && i>0)
.append("text")
.attr("x",function(d,i) {return xScale(d.date);})
.attr("y",function(d,i) {return yScale(d.measurement);})
.text("0") // testing with 0
.style("stroke","white")
.style("font-size","12px")
But when I am running the code, the last 2 blocks despite being the same are generating different outputs. The circles are being generated nicely but when I am appending text to the same block of code, it is beginning later. Unable to understand why so..

Changed .selectAll("circle") to .selelctAll("text") to select each text placeholder rather than circle. Worked
// Using the circle variable again to append text
circles
.selectAll("circle")
.data(function (d){return d.values})
.enter()
.filter((d,i)=>(i%3==0) && i>0)
.append("text")
.attr("x",function(d,i) {return xScale(d.date);})
.attr("y",function(d,i) {return yScale(d.measurement);})
.text("0") // testing with 0
.style("stroke","white")
.style("font-size","12px")
// Using the circle variable again to append text
circles
.selectAll("text")
.data(function (d){return d.values})
.enter()
.filter((d,i)=>(i%3==0) && i>0)
.append("text")
.attr("x",function(d,i) {return xScale(d.date);})
.attr("y",function(d,i) {return yScale(d.measurement);})
.text("0") // testing with 0
.style("stroke","white")
.style("font-size","12px")

Related

dot (symbol) color on d3.js multiline chart

I am trying to replicate this example of a multiline chart with dots. My data is basically the same, where I have an object with name and values in the first level, and then a couple of values in the second level inside values. For the most part, my code works, but for some reason, the j index in the anonymous function for the fill returns an array of repeated circle instead of returning the parent of the current element. I believe this may have something to do with the way I created the svg and selected the elements, but I can't figure it out. Below is an excerpt of my code that shows how I created the svg, the line path and the circles.
var svgb = d3.select("body")
.append("svg")
.attr("id","svg-b")
.attr("width", width)
.attr("height", height)
var gameb = svgb.selectAll(".gameb")
.data(games)
.enter()
.append("g")
.attr("class", "gameb");
gameb.append("path")
.attr("class", "line")
.attr("d", function(d) {return line_count(d.values); })
.style("stroke", function(d) { return color(d.name); })
.style("fill", "none");
gameb.selectAll("circle")
.data(function(d) {return d.values;})
.enter()
.append("circle")
.attr("cx", function(d) {return x(d.date);})
.attr("cy", function(d) {return y_count(d.count);})
.attr("r", 3)
.style("fill", function(d,i,j) {console.log(j)
return color(games[j].name);});
j (or more accurately, the third parameter) will always be the nodes in the selection (the array of circles here), not the parent. If you want the parent datum you can use:
.attr("fill", function() {
let parent = this.parentNode;
let datum = d3.select(parent).datum();
return color(datum.name);
})
Note that using ()=> instead of function() will change the this context and the above will not work.
However, rather than coloring each circle independently, you could use a or the parent g to color the circles too:
gameb.append("g")
.style("fill", function(d) { return color(d.name); })
.selectAll("circle")
.data(function(d) {return d.values;})
.enter()
.append("circle")
.attr("cx", function(d) {return x(d.date);})
.attr("cy", function(d) {return y_count(d.count);})
.attr("r", 3);
Here we add an intermediate g (though we could use the original parent with a few additional modifications), apply a fill color to it, and then the parent g will color the children circles for us. The datum is passed on to this new g behind the scenes.

D3 white tick marks (grid lines) are drawing over text, how to correct?

I am drawing a simple horizontal bar graph in D3.
I'm breaking the bars into smaller rectangles with long, white tick marks.
But my tick marks are slicing through some text.
I suspect I am drawing elements in the wrong order, somehow.
What do you think?
How to make gridlines slice only the bars, not the text?
You can see how it looks tacky, whole thing is here:
http://bl.ocks.org/greencracker/4378b817d4d5393c4e37
Here's the key part, I think:
...
var bar = svg.selectAll("g.bar") // create the bars
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(0," + y(d.name) + ")"; });
bar.append("rect") // draw the bars
.attr("width", function(d) { return x(d.value); })
.attr("height", y.rangeBand())
.style("opacity", 0.5);
svg.append("g") // draw my x axis, which includes the white tickmarks
.attr("class", "x axis")
.call(xAxis);
bar.append("text") // then attach text to each bar
.attr("class", "value")
.attr("x", function(d) { return x(d.value); })
.attr("y", y.rangeBand() / 2)
.attr("dy", ".35em")
.attr("text-anchor", function (d) {
if (d.name === "Business, Management, Marketing") {return "end"}
else {return "start"} ;}) // correct for "business" being very long
.attr("dx", function (d) {
if (d.name === "Business, Management, Marketing") {return -3}
else {return 3} ;}) // correct for "business" being very long
.text(function(d) { return (d.name); });
(Yes, I asked a similar question before, but this one's a little different and I can't figure it out. Transition on axis -- losing grid lines (ticksize), how to transition in correct order?)
your suspicions about drawing elements in the proper order are correct.
What's currently happening is:
1) Append <g class="bar"> for new data.
var bar = svg.selectAll("g.bar")
.data(data)
.enter().append("g")
2) Append a rectangle to the above group.
bar.append("rect")
3) Draw the x-axis, which is placed after (i.e. over) of (1).
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
4) Append/nest text to the group defined in (1).
bar.append("text")
Since the text is nested with the group and the group is inserted into the DOM before the x-axis, the tickmarks are showing up over the text.
However if we do something as simple as say moving the x-axis call before the bar call, the ticks won't show up because they'll be drawn underneath the bars. So unfortunately you'll need to split up the grouping of rectangle and text.
This also means you'll need define a transform/translate for the text labels:
svg.selectAll("text.value")
.data(data)
.enter().append("text")
.attr("class", "value")
.attr("transform", function(d) {
return "translate("+ x(d.value)+5 + "," + (12+y(d.name)) + ")";
})
.attr("text-anchor", function (d) {
if (d.name === "Business, Management, Marketing") {return "end"}
else {return "start"} ;}) // correct for "business" being very long
.attr("dx", function (d) {
if (d.name === "Business, Management, Marketing") {return -3}
else {return 3} ;}) // correct for "business" being very long
.text(function(d) { return (d.name); });
The above should work for all labels except for Business, Management, Marketing, because its translate is at 5800.
Hope this helps!

d3.js - transition starting only without svg title, not with it

I found no direct answer for this, please forgive me if this has been covered differently in another topic.
I draw a bar chart which appears with a transition. I also want to add a tooltip which displays the value of data on mousehover.
Using the code below I have managed to obtain either the tooltip or the transition, but never the 2 together, which is my objective.
chart.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("fill", function(d) {return colorscale(colorize(d.age));})
.attr("x", function(d) {return xscale(d.name);})
.attr("y", height - 3)
.attr("height", 3)
.attr("width", xscale.rangeBand())
.append("title")
.text(function(d){return d.age;})
.transition()
.duration(1600)
.attr("y", function (d) {return yscale(d.age);})
.attr("height", function (d) {return height - yscale(d.age);}) ;
If I remove
.append("title")
.text(function(d){return d.age;})
Then my transition works fine. If I but those 2 lines back I can see my tooltip but I lose my transition.
Any suggestion would be appreciated!
You can see the result here
Thank you
You need to add the transition to the rect and not the title element:
var sel = chart.selectAll(".bar")
.data(data)
.enter()
.append("rect");
sel.append("title")
.text(function(d){return d.age;});
sel.transition()
.duration(1600)
.attr("y", function (d) {return yscale(d.age);})
.attr("height", function (d) {return height - yscale(d.age);}) ;

How do I use enter() correctly in D3.js?

I have some code that create a bunch of circles, updates those circles, removes some of the circles, and lastly I'd like to find out how to use the enter() correctly to add more circles to my current ones.
This is my code for creating circles when there are none:
var circle = SVGbody
.selectAll("circle")
.data(graphDataY/*, function(d){return d;}*/);
circle.enter()
.append("circle");
circle
.attr("cx",xScale(0))
.attr("cy", yScale(minAxisY))
.attr("r",4)
.style('opacity', 0)
.style("fill", colorCircles())
.transition()
.duration(1000)
.attr("cx", function(d, i){ return xScale(graphDataX[i]);})
//.attr("cy", function (d, i){ return yScale(i); })
.style('opacity', 0.8)
.transition()
.duration(1000)
.attr("cy", function(d){return yScale(parseFloat(d));} );
I use the following code to update my circles and remove the ones that aren't being used (sometimes that's the case):
var circle = SVGbody
.selectAll("circle")
.data(graphDataY);
circle
.style("fill", colorCircles())
.transition().duration(1000)
.attr("cx", function(d, i){ return xScale(graphDataX[i]);})
.attr("cy",function(d){return yScale(parseFloat(d));});
circle.exit()
.style("opacity", 1)
.transition()
.duration(300)
.style("opacity", 0)
.remove();
And when lastly, let's assume the last data i used had a length of 100, that means I currently have 100 circles displayed. If I used a new dataset with 120 of length, I use my update code to move the existing circle ( except for the exit().remove() ), and then I try to use the following to "create" the remaining circles out of the remaining unbound data (which in this case would be 20 circles remaining):
circle.enter().append("circle")
.attr("cx", function(d, i){ return xScale(graphDataX[i]);})
.attr("cy",function(d){return yScale(parseFloat(d));})
.style("opacity", 0)
.transition()
.duration(300)
.style("opacity", 1)

Nested SVG node creation in d3.js

I'v just started playing with d3js and find it strange that I have to create multiple selectors for each element I want to link to the background data structure for example separate selectors such as one for overlay text and one for rectangles to make an annotated bar graph.
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr('y',function(d,i){return i*10;})
.attr('height',10)
.attr('width',function(d){return d.interestingValue})
.fill('#00ff00');
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.attr('y',function(d,i){return i*10;})
.fill('#0000ff')
.text(function(d){return d.interestingValue});
Is there a more convenient way of combining these into a single selection and enter() chain that creates both the rects and the text elements?
Use a G (group) element. Use a single data-join to create the G elements, and then append your rect and text element. For example:
var g = svg.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(0," + i * 10 + ")"; });
g.append("rect")
.attr("height", 10)
.attr("width", function(d) { return d.interestingValue; });
g.append("text")
.text(function(d) { return d.interestingValue; });

Categories

Resources