I have a stacked bar chart in d3.js
For every stacked bar i have corresponding text value showing near stack itself.
problem is, some text values displaying are hidden behind bars, where as some are visible over bars. I want all text to visible over my bars. my code looks like,
bar.append("text")
.attr("x", function (d) { return x(d.x); })
.attr("y", function (d) { return y(d.y0 + d.y); })
.attr("dy", ".35em")
.attr('style', 'font-size:13px')
.text(function (d) { if (d.y != 0) { return "$" + d.y; } })
.style('fill', 'black');
Basically the issue related to z-index. But there is no z-index for SVG, so it can be fixed by reordering elements. Details here With JavaScript, can I change the Z index/layer of an SVG <g> element?
The simplest and fastest way:
To add .reverse() to the dataset.
// Create groups for each series, rects for each segment
var groups = svg.selectAll("g.cost")
.data(dataset.reverse())
.enter().append("g")
.attr("class", "cost")
.style("fill", function(d, i) { return colors[i]; });
The better way
To add different containers for bars and labels and put them in the right order in the DOM.
Try it http://jsfiddle.net/kashesandr/z90aywdj/
Related
I have a grouped bar chart design that has each bar amount immediately above the bar instead of the y axis bar on the far left.
I have added the text like so...
svg.selectAll(".text")
.data(data)
.enter()
.append("text")
.attr("class","label")
.attr("x", function(d) { return x1(d.rate) })
//.attr("y", function(d) { return y(d.value) + 1; })
.attr("dy", ".75em")
.text(function(d) {return d.value; });
But i cant seem to obtain the x and y values according to the bar position and the actual text of the d.value isn't getting inserted.
Sample here:
https://jsfiddle.net/6p7hmef1/7/
That's because the data bound to the texts that you're adding isn't right.
If you add a console.log as follows, you'll be able to see why the labels aren't being inserted. Check for the outputs in console. Console log fiddle
.text(function(d) {console.log(d); return d.value; });
One approach would be to append the texts to the bar groups ("slice" in your case). Just like you do for the bars. Here's a fiddle doing this:
JS Fiddle Demo
slice.selectAll(".text")
.data(function(d) { return d.values; })
.enter()
.append("text")
.attr("class","label");
slice.selectAll('text.label')
.attr("x", function(d) { return x1(d.rate)+ x1.rangeBand()/3 })
.attr("y", function(d) { return y(d.value) + 1; }).attr('dy', '-0.4em')
.text(function(d) {return d.value; });
This might look weird as the texts are shown before the transition of the bars. So changing the value of texts once the bars are shown seems like the right approach to me. (dx and dy attributes can be adjusted as per the requirement)
Here's the final fiddle:
JS FIDDLE
I'm using the "end" callback for every transition and changing the text values accordingly.
.each('end', function () {
d3.select(this.parentNode).selectAll('text.label')
.attr("x", function(d) { return x1(d.rate)+ x1.rangeBand()/3 })
.attr("y", function(d) { return y(d.value) + 1; }).attr('dy', '-0.4em')
.text(function(d) {return d.value; });
})
Hope this helps. :)
Let me know if any part of the code isn't understandable.
Is it possible to add a piece of text in the centre of multiple rectangles in a D3 treemap chart? So what i'm after is something like this:
As you can see by the picture, I want to have text appear once in the middle of each “section” (different colours) on the chart. Is it possible to do this with D3? If so how would I achieve it.
Currently I've managed to have it appear on each individual rectangle, like so:
cell.append("svg:text")
.attr("x", function(d) { return d.dx / 2; })
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.text("test")
.style("opacity", function(d) { console.log(this.getComputedTextLength());d.w = this.getComputedTextLength(); return d.dx > d.w ? 1 : 0; });
Full code is here: http://jsfiddle.net/noobiecode/9ev9qjt3/74/
Any help will be appreciated.
For appending these texts, we first need to find the corresponding data. Right now, nodes have data regarding all the different depths (parents and children), but for these texts we only need the data for the second depth (depth == 2).
So, we first get the data:
var parents = treemap.nodes(root)
.filter(function(d) { return d.depth == 2 });
It gives us an array with three objects, so we are in the right way. This array (parents) have all we need to position the texts (x, y, dx, dy, name).
Then, we bind this data to the texts:
var parentsText = svg.selectAll(".parentsText")
.data(parents)
.enter()
.append("text");
After that, we append the texts:
parentsText.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.attr("x", function(d){ return d.x + d.dx/2})
.attr("y", function(d){ return d.y + d.dy/2})
.text(function(d){ return d.name})
.attr("class", "parentText");
And this is the resulting fiddle: http://jsfiddle.net/gerardofurtado/9ev9qjt3/94/
You can also have the texts for each individual rectangle and for the group (but, in that case, you may want to move away some overlapping texts): http://jsfiddle.net/gerardofurtado/9ev9qjt3/95/
I have put together a D3 line chart and added threshold encoding using clip path / clipping. The only problem I am facing is I am not able to add tooltips to this chart. I want a tooltip when I hover anywhere in the chart and the corresponding y axis value on the chart shows up in the tooltip.
I have added threshold encoding using this example by Mike Bostock.
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return _config.xScale(d.vtc); })
.y(function(d) { return _config.yScale(d.values); });
svg.append("clipPath")
.attr("id", "clip-above")
.append("rect")
.attr("width", _config.width)
.attr("height", _config.yScale(55));
svg.append("clipPath")
.attr("id", "clip-below")
.append("rect")
.attr("y", _config.yScale(55))
.attr("width", _config.width)
.attr("height", _config.height - _config.yScale(55));
svg.selectAll(".line")
.data(["above", "below"])
.enter().append("path")
.attr("class", function(d) { return "line " + d; })
.attr("clip-path", function(d) { return "url(#clip-" + d + ")"; })
.datum(data)
.attr("d", line);
I didn't know how to go about adding a tooltip for this particular chart as there is clip rectangle over the path and the path is broken down into above and below segment to give the colour effects.
Do we have a unified way to add a tooltip to normal path and this one? If yes I would like to know some sources/links I can look at.
Something like this, but not that complicated (without any indicator on the line, just the tooltip)
My CODEPEN LINK
You can add mouseOver handler for the line and translate back the mouse y position to yAxis value using the .invert function of d3 linear scale. Now, you can dynamically add a tooltip text element and set the position, value to it
Here is the updated Codepen link
NOTE: You still need to increase the capture area of the line. This can be done by adding a transparent stroke to the line.
svg.selectAll(".line")
.data(["above", "below"])
.enter().append("path")
.attr("class", function(d) { return "line " + d; })
.attr("clip-path", function(d) { return "url(#clip-" + d + ")"; })
.datum(data)
.attr("d", line)
.on("mouseover", function() {
var mousePos = d3.mouse(this);
var yAxisValue = _config.yScale.invert(mousePos[1]);
svg.selectAll(".tooltip").data([mousePos])
.enter().append("text")
.classed("tooltip", true)
.attr("x", function(d) { return d[0]})
.attr("y", function(d) { return d[1]})
.text(yAxisValue);
})
.on("mouseout", function() {
svg.selectAll(".tooltip").data([]).exit().remove();
});
I am getting started with D3 and SVG but I haven't found anything clear on how to add hyperlinks. Here is some code I have to write labels to the left of the bars in a D3 bar chart. Is there a good sample somewhere to convert these labels to hyperlinks (say objects in rangeData had an href and name/label property)? I searched around a bit but haven't gotten much further than the svg spec for adding an anchor element.
chart.selectAll(".bar.barLabel")
.data(rangeData)
.enter().append("text")
.attr("class", "bar")
.attr("x", 0)
.attr("y", function (d, i) { return height(i) + barHeight(y, i) / 2; })
.attr("dx", -20)
.attr("dy", ".35em")
.attr("text-anchor", "end")
.text(function (d) { return d.label; });
You can use the a element to achieve this, very similar to HTML itself. You wrap the content in the a element and provide the link target as the href attribute with xlink namespace.
chart.selectAll("a")
.data(rangeData)
.enter()
.append("a")
.attr("xlink:href", function(d) { return d.href; })
.append("text")
.text(function (d) { return d.label; });
Alternatively, you could use the foreignObject element to directly embed HTML into your SVG.
I'm new to d3.js (and stackoverflow) and I'm currently working through the parallel coordinates example. I'm currently using a 2d array named 'row' for the data. Above each vertical axis is the label '0' or '1' or '2', etc. However, I'd like each vertical axis to be labeled with the text in row[0][i]. I believe the numbers 0,1,2 are coming from the datum. Any suggestions on how I may use the labels in row[0][i] instead? I suspect I'm doing something wrong that's pretty basic. Here's the relevant code. Thanks !
// Extract the list of expressions and create a scale for each.
x.domain(dimensions = d3.keys(row[0]).filter(function (d, i) {
return row[0][i] != "name" &&
(y[d] = d3.scale.linear()
.domain(d3.extent(row, function (p) { return +p[d]; }))
.range([height, 0]));
}));
// Add a group element for each dimension.
var g = svg.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function (d) { return "translate(" + x(d) + ")"; });
// Add an axis and title.
g.append("g")
.attr("class", "axis")
.each(function (d) { d3.select(this).call(axis.scale(y[d])); })
.append("text")
.attr("text-anchor", "middle")
.attr("y", -9)
.text(String);//.text(String)
If you only have a controlled set of Axis (like three axis), you may just want to set them up, individually, as follows...
svg.append("text").attr("class","First_Axis")
.text("0")
.attr("x", first_x_coordinate)
.attr("y", constant_y_coordinate)
.attr("text-anchor","middle");
svg.append("text").attr("class","Second_Axis")
.text("1")
.attr("x", first_x_coordinate + controlled_offset)
.attr("y", constant_y_coordinate)
.attr("text-anchor","middle");
svg.append("text").attr("class","Third_Axis")
.text("2")
.attr("x", first_x_coordinate + controlled_offset*2)
.attr("y", constant_y_coordinate)
.attr("text-anchor","middle");
However, if you have dynamically placed axis that rely on the data, you may want to place the axis info using a function that holds the y coordinate constant while determining the x coordinate based on a fixed "offset" (i.e. data driven axis placement). For example...
svg.append("text")
.attr("class",function(d, i) {return "Axis_" + i; })
.text(function(d,i) {return i; })
.attr("x", function(d, i) { return (x_root_coordinate + x_offset_value*i); })
.attr("y", constant_y_coordinate)
.attr("text-anchor","middle");
I hope this helps.
Frank