I'm working on a fairly basic bar chart where I'm trying to have a span icon that appears, anchored at the start of each bar. Which icon appears is dependent on the class of the bar. For example, if the bar is blue, I want a certain icon vs. if the bar is red.
I've appended and added the span which shows up in the console, but is not actually appearing any where in the chart.
I have the icons stored as spans in my css, one for each version of the value name that gets plugged in.
I've tried a variety of selections, ordering, etc. But can't get it to stick.
var bars = svg.selectAll('.bar')
.data(data)
.enter().append('g')
.attr('class', 'bar');
bars.append('rect')
var icons = svg.selectAll('rect')
.data(data)
.enter().append("span")
.attr("class", function(d, i) {
return "icon-" + d.value + "-right";
})
.attr('dx', -6)
.attr('dy', (bar_height / 2) +5)
.attr('text-anchor', 'start');
You should use foreignObject element to insert HTML into SVG.
Like so:
var icons = svg.selectAll('foreignObject').data(data);
icons.enter().append("foreignObject")
.attr("class", function(d) { return "icon-" + d.value + "-right"; })
.append("xhtml:body")
.append("xhtml:span");
Also you can use text element to add icons to the SVG:
var icons = svg.selectAll('text').data(data);
icons.enter().append("text")
.html("") // utf-8 character for the icon
Related
This D3 js example shows all the code to produce a multi-line graph that can be toggled. Each line in the graph includes dots for existing data points.
While the lines can be toggled on/off, the dots stay stagnant. I would like for the toggle to work for both turning on/off the line & the dots that are associated with the same line.
I suspect that the svg.append("text") is the part that requires code update to also enable the dots to be turned on/off along with the line.
Here is the existing code snipet that turns on/off the line graph, but it doesn't turn on/off the dots.
svg.append("text")
.attr("x", (legendSpace/2)+i*legendSpace) // space legend
.attr("y", height + (margin.bottom/2)+ 5)
.attr("class", "legend") // style the legend
.style("font-size","15px") // Change the font size
.style("font-weight", "bold") // Change the font to bold
.style("text-anchor", "middle") // center the legend
.style("fill", function() { // Add the colours dynamically
return d.color = color(d.key); })
.on("click", function(){
// Determine if current line is visible
var active = d.active ? false : true,
newOpacity = active ? 0 : 1;
// Hide or show the elements based on the ID
d3.select("#tag"+d.key.replace(/\s+/g, ''))
.transition().duration(100)
.style("opacity", newOpacity);
// Update whether or not the elements are active
d.active = active;
})
.text(d.key);
Please help.
IDs are unique. You cannot set the same ID for several different DOM elements.
Solution: set classes instead.
For the lines:
.attr("class", 'tag'+d.key.replace(/\s+/g, ''))
And for the circles:
.attr("class", d=>'tag'+d.symbol.replace(/\s+/g, ''))
Then, get the class on the click event (use selectAll, not select):
d3.selectAll(".tag"+d.key.replace(/\s+/g, ''))
here is the updated fiddle: http://jsfiddle.net/gx4zc8tq/
I have a scrolling line chart that is being updated in realtime as data comes in. Here is the js fiddle for the line chart. https://jsfiddle.net/eLu98a6L/
What I would like to do is replace the line with a dot graph, so each point that comes in would create a dot, and the scrolling feature is maintained.This is the type of chart I would like to create dow jones dot graph and ultimately I would like to remove the line underneath.
This is the code I have used to try and add dots to my graph.
g.append("g")
.selectAll("dot")
.data(4)
.enter("circle")
.attr("cx", "2")
.attr("cy", "2");
So far I haven't had any success. I'm very new to d3, so any help is appreciated. Thanks!
Based on your code an approach for this can be :
var circleArea = g.append('g'); /* added this to hold all dots in a group */
function tick() {
// Push a new data point onto the back.
data.push(random());
// Redraw the line.
/* hide the line as you don't need it any more
d3.select(this)
.attr("d", line)
.attr("transform", null);
*/
circleArea.selectAll('circle').remove(); // this makes sure your out of box dots will be remove.
/* this add dots based on data */
circleArea.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('r',3) // radius of dots
.attr('fill','black') // color of dots
.attr('transform',function(d,i){ return 'translate('+x(i)+','+y(d)+')';});
/* here we can animate dots to translate to left for 500ms */
circleArea.selectAll('circle').transition().duration(500)
.ease(d3.easeLinear)
.attr('transform',function(d,i){
if(x(i )<=0) { // this makes sure dots are remove on x=0
d3.select(this).remove();
}
return 'translate('+x(i-1)+','+y(d)+')';
});
/* here is rest of your code */
// Slide it to the left.
d3.active(this)
.attr("transform", "translate(" + x(-1) + ",0)")
.transition()
.on("start", tick);
// Pop the old data point off the front.
data.shift();
}
See it in action : https://codepen.io/FaridNaderi/pen/weERBj
hope it helps :)
I have been trying to implement D3.js donut with multiple rings. But, the problem is with click event as it works fine with click on first ring but, show weird behavior while clicking on the second ring. Also it shows some weird problems with mousehover as well.
{"metaData":null,
"data":{graphDetails":[{"displayName":"MUW","name":"DEF","score":5},{"displayName":"ABC","name":"ABCD","score":15},{"displayName":"DEFA","name":"DEF","score":35}],"graphOneDetails":[{"displayName":"D1","name":"D1","score":11},{"displayName":"D2","name":"D2","score":25},{"displayName":"D3","name":"D3","score":22}]},"success":true}
//Define arc ranges
var arcText = d3.scale.ordinal().rangeRoundBands([0, width], .1, .3);
// Determine size of arcs
var arc = d3.svg.arc().innerRadius(radius - 75).outerRadius(radius - 25);
var arc_2= d3.svg.arc().innerRadius(radius - 25).outerRadius(radius);
//Create the donut pie chart layout
d3.layout.pie().value(function(d){
return d["score"];
}).sort(null);
//Append SVG attributes and append g to the SVG
d3.select("#donut-chart")
.attr("width", width)
.attr("height",height)
.append("g")
.attr("transform","translate("+radius+","+radius+")");
//Define Inner Circle
svg.append("circle")
.attr("cx",0)
.attr("cy",0)
.attr("r",280)
.attr("fill","#fff");
//Calculate SVG paths and fill in the colors
var div = d3.select("body")
.append("div")
.attr("class","tooltip")
.style("opactiy",0);
// Append First Chart
var g = svg.selectAll(".arc").data(pie($scope.categories))
.enter()
.append("g")
.attr("class","arc")
.on("click",function(d, i){
alert(d.data.name)
}).on("mouseover",function(d){
alert(d.data.name);
}).on("mouseout",function(d){
alert(d.data.name);
});
g.append("path")
.attr("d",arc)
.attr("fill","#024");
g.append("text")
.attr("transform", function(d){
return "translate("+arc.centroid(d)+")";
}).attr("dy",".35em")
.style("text-anchor","middle")
.attr("fill","#fff")
.text(function (d,i){
return d.data.displayName
});
g.selectAll(".arc text").call(wrap.arcText.rangeBand());
//Append Second Chart
g.data(pie($scope.divisions)).append("path")
.attr("d",arc_2)
.attr("fill","#eee");
g.on("click, function(d,i){
alert(d.data.name);
}).on("mouseover", function(d){
alert(d.data.name);
});
//Append text to second chart
g.append("text")
.attr("transform", function(d){
return "translate("+arc_2.centroid(d)+")";
}).attr("dy",".35em")
.style("text-anchor","middle")
.attr("fill","#fff")
.text(function (d,i){
return d.data.displayName
});
g.selectAll(".arc text").call(wrap.arcText.rangeBand());
In initial state it works fine, but, when I click one chart it displays the data correctly. And when I click inner chart and updates my json to
{"metaData":null,
"data":{graphDetails":[{"displayName":"MUW","name":"DEF","score":5},{"displayName":"DEFA","name":"DEF","score":35}],"graphOneDetails":[{"displayName":"D1","name":"D1","score":11},{"displayName":"D3","name":"D3","score":22}]},"success":true}
Then it display inner chart as a full donut but, the outer chart comes as an arc instead of full donut. Same problem is happening with the mouse over as while I am hovering over the second chart each and everything comes correctly as a tool-tip. (I didn't include the code of tool-tip). But, I mouse over on ABC and returns me DEFA. So, I think there must be something related to the way I have appended these two arcs.
EDIT 1
Created the JSFidle, with my dataset and it's not showing anything
http://jsfiddle.net/pcr3ogt4/
I'm creating a simple chart with d3.js using the following code. Now suppose the var data is properly formatted (ex. "[20,15,15,40,10]"), the horizontal chart displays properly but I'd like to add some labels on the left and I'm a bit overwhelmed by d3.js . I was trying to insert an extra div containing the label before every other div representing and showing the data, but I can't understand how or if that's the proper way.
The result should look something like:
Label 1 |=============================40%==|
Label 2 |=================30%==|
Label 3 |======15%==|
and so on. The bars display just fine, but how do I add the labels on the left ? This is the piece of script that displays the bars (given a div with id 'chart' and the proper css).
chart = d3.select('#chart')
.append('div').attr('class', 'chart')
.selectAll('div')
.data(data).enter()
.append('div')
.transition().ease('elastic')
.style('width', function(d) { return d + '%'; })
.text(function(d) { return d + '%'; });
You'll find a working example here . So, how do I add labels on the left of every bar so that the user knows what every bar represents ?
I think your idea of appending a <div> containing a label before each bar is reasonable. To do so, you first need to append a <div> for each bar, then append a <div> containing the label, and finally append a <div> containing the bars. I've updated your example with a JSFiddle here, with the Javascript code below (some changes were also required to the CSS):
// Add the div containing the whole chart
var chart = d3.select('#chart').append('div').attr('class', 'chart');
// Add one div per bar which will group together both labels and bars
var g = chart.selectAll('div')
.data([52,15,9,3,3,2,2,2,1,1]).enter()
.append('div')
// Add the labels
g.append("div")
.style("display", "inline")
.text(function(d, i){ return "Label" + i; });
// Add the bars
var bars = g.append("div")
.attr("class", "rect")
.text(function(d) { return d + '%'; });
// Execute the transition to show the bars
bars.transition()
.ease('elastic')
.style('width', function(d) { return d + '%'; })
Screenshot of JSFiddle output
I know how to add text element to simple node (append text). The problem is when I would like to add text to path surrounding several nodes. I have created example on http://jsfiddle.net/FEM3e/5/ Please ignore nodes in upper left corner. So I have two groups of nodes. And I would like to add text for each group. Printscreen of desired output http://dopisna.bencin.si/screenshot.png.
I set path in
force.on("tick", function () {
node.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
vis.selectAll("path")
.data(groups)
.attr("d", singlePath)
.enter().insert("path", "g")
.style("fill", groupFill)
.style("stroke", groupFill)
.style("stroke-width", 57)
.style("stroke-linejoin", "round")
.style("opacity", .7);
});
I have tried appending text with no success. I am asking for some hints.
OK then. The problem is that you're using text instead of textPath. I've modified your fiddle and now there's some text, albeit some rather ugly text, appended to your path.
The only real change I've made is the addition of this snippet:
vis.selectAll("text")
.data(groups)
.enter()
.append("text")
.attr("x", 8)
.attr("dy", 28)
.append("textPath")
.attr("xlink:href", function (d,i) { return "#path_" + i; })
.text(function (d,i) { return "path_" + i; });
You can see that you go through the usual selection and data binding. You then append your text with the attributes you want (definitely change the ones I borrowed from Mikes Bl.ock) and then you append the text path linking it to a path element in the xlink:href attribute. Obviously you then create some text. One of the cool things about textPath is that it allows you append curved paths.
I think that there's a bit of overkill using the groups as data for the textPath, so you might want to select a more appropriate data selection to bind to this.