I would like to show a text on mouseover.
var circle = elemEnter.append("circle")
.attr("r", function(d){return d.r*2} )
.attr("dx", function(d){return d.x} )
.attr("stroke","gray")
.attr("fill", "#91c6ed")
.on("mouseover", function()
{d3.select(this).style("stroke", "#ff7f0e");
d3.select(this).style("stroke-width", "2px");
elemEnter.append("text")
.text(function(d){return d.name})})
.on("mouseout", function()
{d3.select(this).style("stroke", "gray");
d3.select(this).style("stroke-width", "1px");});
This piece of code works but show all the names on all the circles and when I try to replace
elemEnter.append("text").text(function(d){return d.name})
by
d3.select(this).append("text").text(d.name)
nothing happens.
I think it is possible to do it but I don't know what I'm doing wrong.
You can't append text to a circle. You need to start with a g and append the circle to the g and append the text to the g. Keep in mind that the g will not have cx/cy attributes and so you'll need to put that data into the following syntax:
.attr("transform", function (d) {return "translate("+d.X+","+d.Y")"})
If you're binding data, bind it to the g and then just naked append the circle and text to the g.
Related
I'm using the following code:
// Add title to y axis
svg.append("text")
.attr("x", -500)
.attr("y", -100)
.attr("text-anchor", "left")
.attr("transform", "rotate(270)")
.style("font-size", "16px")
.text("Test");
How can I have the text to be derived from the result of a function instead of being a string literal? Any help would be appreciated. Thanks.
You can just pass in a function .text, e.g. to set the text value to be the time at execution:
.text(function(){ return new Date(); })
If you want the text value to depend on data, then you need to bind data to the text element first.
I'm very new to d3, so goal #1 was to show the chart. This works with the following code:
const line = d3.line()
.x(d => this.x(d.x))
.y(d => this.y(d.y));
console.log('data', JSON.stringify(data), 'color', color);
// data [{"x":"2017-07-01T04:00:00.000Z","y":81.2},{"x":"2017-08-01T04:00:00.000Z","y":79.6},{"x":"2017-09-01T04:00:00.000Z","y":79.4},{"x":"2017-10-01T04:00:00.000Z","y":80.6},{"x":"2017-11-01T04:00:00.000Z","y":80},{"x":"2017-12-01T05:00:00.000Z","y":76}] color blue
g.append('path')
.datum(data)
.attr('class', 'line')
.attr('stroke', color)
.attr('d', line);
With this code, anytime I run this method again, I get a new line, which is expected since I'm appending. I want to update only the stroke and d attributes when I have new data and/or color, I replace the code after the console.log with:
const lines = g.selectAll('.line').data(data);
lines.enter()
.append('path')
.attr('class', 'line');
lines
.attr('stroke', color)
.attr('d', line);
I don't see the line anymore, not at first, not after updates.
I'm including a codepen with the code in this question: https://codepen.io/anon/pen/BJaVNM?editors=0010
Thanks again for your help!
The proper data join would be:
// give data an array, each part of the array is an array representing a line (ie path)
let lines = g.selectAll('.line').data([data]); //<-- notice array
// you have lines entering, .merge this back to the update selection
lines = lines.enter()
.append('path')
.attr('class', 'line')
.merge(lines);
// lines variable is now enter + update
lines
.attr('stroke', color)
.attr('d', line);
Updated codepen
I am trying to create a tooltip for my line chart that sends out dotted lines to the x and y axis (identical to this d3 n00b example, but my chart has multiple lines) http://www.d3noob.org/2014/07/my-favourite-tooltip-method-for-line.html
I have tried setting up two focus groups, tried adding the extra line info into the existing group, but all I can get is the date running on both lines but the dotted lines and data info only work on one.
Any help would be gratefully accepted.
Here is the chart with code underneath:
http://bl.ocks.org/anonymous/d1dbc221f95f6308b351
This is now fixed, renaming the two focus groups worked
Correct code:http://bl.ocks.org/anonymous/b77e904f34aa2162e1df
You are using the same variable for the two focus groups:
var focus = svg.append("g") // tooltip
.style("display", "none");
var focus = svg.append("g") // tooltip
.style("display", "none");
Name one variable focus1 and the other focus2 to make sure that they remain separate.
I would also recommend refactoring the code such that you don't have to call the exact same functions on both of these, once for the CPI line and once for RPIJ line. Instead, you should create a function which calls the required functions on a focus. You should pass the function which line to attach to and the correct focus group, i.e. focus1 and focus2.
Update for the edit:
As I understand it, you have now added text.y1, text.y2, text.y3 and text.y4 to both the focus groups and are effectively hiding the unnecessary information by setting the correct strokes to #fff here:
// place the value at the intersection RPIJ
focus2.append("text")
.attr("class", "y1")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "-.3em");
focus2.append("text")
.attr("class", "y2")
.attr("dx", 8)
.attr("dy", "-.3em");
// place the date at the intersection RPIJ
focus2.append("text")
.attr("class", "y3")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "1em");
focus2.append("text")
.attr("class", "y4")
.attr("dx", 8)
.attr("dy", "1em");
However, while doing it you have committed a couple of unique mistakes in each case.
For focus1, you do not create a text.y4 at all while for focus2, you update text.RPIJ instead of text.y3:
focus2.select("text.RPIJ")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.RPIJ) + ")")
.text("£" + d.RPIJ);
I'm basically using a modified version of : http://dimplejs.org/advanced_examples_viewer.html?id=advanced_bar_labels .
I'd like to be able to add for each value a border on the left as high as the value (with a specific color for that border).
I'm not really sure where to start for adding that.
Any ideas?
Thanks.
More details : This is what I'd like to obtain : https://dl.dropboxusercontent.com/u/2227188/Image%202.png - the border on the left is the issue. (jsfiddle.net/mkzTk/5/ this what I currently have which is pretty much what's in the example - I don't know where to start really for adding a border)
You could append a rectangle after drawing for each element of the series as follows:
mySeries.afterDraw = function (s, d) {
var shape = d3.select(s);
svg.append("rect")
.attr("x", shape.attr("x"))
.attr("y", shape.attr("y"))
.attr("height", shape.attr("height"))
.attr("width", "10px")
.style("fill", shape.style("stroke"))
.style("pointer-events", "none");
};
The example you mention already uses the afterDraw function so just add the contents above to the existing method for labelling.
It looks nice, here's an example:
http://jsbin.com/lorin/9/edit?js,output#J:L20
I would set up each bar + edge pair as its own group based on a certain data point, and then append two rect elements to that group. Differences in color can be used to give them their distinctive colors.
Your code would look something like this:
var monthBars = d3.selectAll('.monthBar') //These will be for each chart
.data(allMyData, idFunction) //Assign and key your data
.enter()
.append('g')
.classed('monthBar', true);
.each(function(d){
var taskGroups = d3.select(this).selectAll('.taskGroup')
.data(d.dataForThisMonth, taskIdFn)
.enter()
.append('g')
.classed('.taskGroup', true);
.attr('transform', ...) //Define the x and y positioning for the group
taskGroups.append('rect')
//Make this the 'body' rect with the text in it
taskGroups.append('rect')
//Make this the edge rect
})
I have 2 charts on a page (A and B) and wish for some custom behavior to be added to chart B when a brush filter on chart A is performed.
I thought I could achieve this by doing something like;
charta.on('postRender', function(){
...
d3.selectAll('#chartb svg')
.data(nested, function(d){ return d.key})
.enter()
.append("g")
.attr("class", "aclass")... more code....
But this #chartb selector doesn't seem to work - when I inspect the DOM it has appended the <g> attributes to the <html> element and not the svg element I wanted to append to.
Is what I am trying to achieve possible?
If you are just adding stuff to the other chart, something like this should be possible. I don't think you will be able to select the generated items of the other chart and then apply a d3 join to it, because it is already joined.
I believe the problem with the code above is that d3.select is what you use to choose the context for a join, and d3.selectAll is what you use to actually make the data join. See
http://bost.ocks.org/mike/join/
So your code is trying to join to the chart and svg elements, which would have the effect you are describing. Instead you'll want to d3.select the svg and then d3.selectAll the elements you want to add - even though they don't exist yet! Yes, it's kind of a mind-bender; take a look at the above and the linked articles to get a better idea of it.
Note: there are dc convenience methods on the chart object which will execute the selects in the right context.
I got this working in the end by replacing the .enter() with repeated calls to datum() instead. A bit of a hack, but it works; If anyone can suggest a more d3ish way of acheiving this, I would be very grateful.
var svg = chart.svg();
nested.forEach(function(withValues) {
_(withValues.values).filter(function(d){return d.value < threshold}).forEach(function(timesMatchingThreshold){
svg.datum(timesMatchingThreshold)
.append("rect")
.style("opacity", 0.6)
.attr("transform", "translate(" + 30 + ", " + (-30) + ")")
.attr("class", "belowThreshold")
.attr("x", function(d) {return x(d.date)})
.attr("y", function(d) {return 200 - y(d.value)})
.attr("width", 3)
.attr("height", function(d) {return y(d.value)});
});