D3.js horizontal bar chart and data from json file - javascript

I have created a horizontal bar chart using d3.js,the data is also inside the js file only.
But now i want to have a separate json file,from there i need to retrieve the data and use in js file.
I have tried but did not able to access the data from json file.
Here is the code.
Html
<head>
<title>Enjalot's Bar</title>
<script src="d3.v3.min.js"></script>
</head>
JavaScript
<script type="text/javascript">
var data= [{"label":"1990", "value":16},
{"label":"1991", "value":56},
{"label":"1992", "value":7},
{"label":"1993", "value":60},
{"label":"1994", "value":22}
];
var data_max = 60,
num_ticks = 6,
left_margin = 60,
right_margin = 60,
top_margin = 30,
bottom_margin = 0;
var w = 200, //width
h = 200, //height
color = function(id) { return '#00b3dc' };
var x = d3.scale.linear()
.domain([0, data_max])
.range([0, w - ( left_margin + right_margin ) ]),
y = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangeBands([bottom_margin, h - top_margin], .5);
var chart_top = h - y.rangeBand()/2 - top_margin;
var chart_bottom = bottom_margin + y.rangeBand()/2;
var chart_left = left_margin;
var chart_right = w - right_margin;
var vis = d3.select("body")
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("id", "barchart")
.attr("class", "barchart")
var rules = vis.selectAll("g.rule")
.data(x.ticks(num_ticks))
.enter()
.append("svg:g")
.attr("transform", function(d)
{
return "translate(" + (chart_left + x(d)) + ")";});
rules.append("svg:line")
.attr("class", "tick")
.attr("y1", chart_top)
.attr("y2", chart_top + 4)
.attr("stroke", "#939597");
rules.append("svg:text")
.attr("class", "tick_label")
.attr("text-anchor", "middle")
.attr("y", chart_top + 3)
.attr("fill","#939597")
.attr("font-size","0.667em")
.text(function(d)
{
return d;
});
var bbox = vis.selectAll(".tick_label").node().getBBox();
vis.selectAll(".tick_label")
.attr("transform", function(d)
{
return "translate(0," + (bbox.height) + ")";
});
var bars = vis.selectAll("g.bar")
.data(data)
.enter()
.append("svg:g")
.attr("class", "bar")
.attr("transform", function(d, i) {
return "translate(0, " + y(i) + ")"; });
bars.append("svg:rect")
.attr("x", right_margin)
.attr("width", function(d) {
return (x(d.value));
})
.attr("height", y.rangeBand())
.attr("fill", color(0))
.attr("stroke", color(0));
//Labels
var labels = vis.select("g.bar")
.append("svg:text")
.attr("class", "label")
.attr("x", 0)
.attr("text-anchor", "right")
.attr("transform", "rotate(270)")
.attr("y", 40)
.attr("x", -55)
.attr("dy", ".71em")
.text("Depts")
.style({"text-anchor":"end","font-size":"0.667em","fill":"#939597"});
var bbox = labels.node().getBBox();
labels = vis.selectAll("g.bar")
.append("svg:text")
.attr("class", "value")
.attr("fill","#fff")
.attr("font-size","0.667em")
.attr("x", function(d)
{
return 65;
})
.attr("text-anchor", "start")
.text(function(d)
{
return "" + d.value + "%";
});
bbox = labels.node().getBBox();
vis.selectAll(".value")
.attr("transform", function(d)
{
return "translate(0, " + (y.rangeBand()/2 + bbox.height/4) + ")";
});
//Axes
vis.append("svg:line")
.attr("class", "axes")
.attr("x1", chart_left)
.attr("x2", chart_left)
.attr("y1", chart_bottom)
.attr("y2", chart_top)
.attr("stroke", "#939597");
vis.append("svg:line")
.attr("class", "axes")
.attr("x1", chart_left)
.attr("x2", chart_right+120)
.attr("y1", chart_top)
.attr("y2", chart_top)
.attr("stroke", "#939597");
</script>

You'll need something like this to access a JSON file
d3.json("path/to/file.json", function(error, json) {
// since this function is asynchronous,
// you can't access data outside it
// So do pass the json into the the function that needs it
if (error) return console.warn(error);
someFunc(json);
});
function someFunc(data) {
....
}
Read this wiki for xhr request in D3.

Related

D3.js add circles to every other point on a line graph

I've got a line chart that needs circles on every other point for column A and C (not for column B). I've struggled to figure out how to do it. This is my line chart without the circles:
date,A=count,A=rank,B=count,B=rank,C=count,C=rank
2016-11-01,60588,213,51915,46,41200,10
2016-12-01,73344,216,58536,47,41230,10
2017-01-01,64164,219,53203,50,51220,12
2017-02-01,85295,224,34047,52,61000,15
2017-03-01,86089,226,44636,54,71200,16
2017-04-01,96871,230,55281,55,71000,10
2017-05-01,97622,234,85879,55,67900,10
I've tried dozens of solutions and I'm very stuck! Here is one of the things I've tried:
linesAndDots.selectAll("line-circle")
.data(data)
.enter().append("circle")
.attr("class", "data-circle")
.attr("r", 5)
.attr("cx", function(d) { return xScale(d.date); })
.attr("cy", function(d) { return yScale(d.measurement); });
But that is giving back NaN for the cx and cy values.
My full code looks like this:
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
path.line-0 {
fill: none;
stroke: #1F77B4;
}
path.line-1 {
fill: none;
stroke: #FF7F0E;
}
</style>
</head>
<!-- Body tag is where we will append our SVG and SVG objects-->
<body>
</body>
<!-- Load in the d3 library -->
<script src="lib/d3.v5.min.js"></script>
<div id="svgdiv"></div>
<script>
//------------------------1. PREPARATION------------------------//
//-----------------------------SVG------------------------------//
var columns=['A=count','B=count'];
var columnsB=['A=rank','B=rank'];
var width = 960;
var height = 500;
var margin = 5;
var padding = 5;
var adj = 75;
// we are appending SVG first
var svg = d3.select("body").append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "-"
+ adj + " -"
+ adj + " "
+ (width + adj *3) + " "
+ (height + adj*3))
.style("padding", padding)
.style("margin", margin)
.classed("svg-content", true);
//-----------------------------DATA-----------------------------//
var timeConv = d3.timeParse("%Y-%m-%d");
var formatTime = d3.timeFormat("%b %y")
var dataset = d3.csv("toShare.csv");
dataset.then(function(data) {
console.log(data.columns.slice(1))
var slices = columns.map(function(id) {
return {
id: id,
values: data.map(function(d){
return {
date: timeConv(d.date),
measurement: +d[id]
};
})
};
})
//----------------------------SCALES----------------------------//
var xScale = d3.scaleTime().range([0,width]);
var yScale = d3.scaleLinear().rangeRound([height, 0]);
xScale.domain(d3.extent(data, function(d){
return timeConv(d.date)}));
yScale.domain([(0), d3.max(slices, function(c) {
return d3.max(c.values, function(d) {
return d.measurement + 4; });
})
]);
//-----------------------------AXES-----------------------------//
var yaxis = d3.axisLeft()
.ticks(9)
.scale(yScale);
var xaxis = d3.axisBottom()
.ticks(7)
.scale(xScale);
//----------------------------LINES-----------------------------//
var line = d3.line()
.x(function(d) { return xScale(d.date); })
.y(function(d) { return yScale(d.measurement); });
let id = 0;
var ids = function () {
return "line-"+id++;
}
//-------------------------2. DRAWING---------------------------//
//-----------------------------AXES-----------------------------//
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xaxis)
.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
50 + ")")
.style("text-anchor", "middle")
.text("Month");
svg.append("g")
.attr("class", "axis")
.call(yaxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - adj)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Running Total");
//----------------------------LINES-----------------------------//
var linesAndDots = svg.selectAll("lines")
.data(slices)
.enter()
.append("g");
linesAndDots.append("path")
.attr("class", ids)
.attr("d", function(d) { return line(d.values); });
linesAndDots.selectAll("line-circle")
.data(data)
.enter().append("circle")
.attr("class", "data-circle")
.attr("r", 5)
.attr("cx", function(d) {
console.log("id", id)
return 5; })
.attr("cy", function(d) { return 40; });
//Add the label on the right
linesAndDots.append("text")
.attr("class", ids)
.datum(function(d) {
return {
id: d.id,
value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) {
return "translate(" + (xScale(d.value.date) + 10)
+ "," + (yScale(d.value.measurement) + 5 ) + ")"; })
.attr("x", 5)
.text(function(d) { return d.id.replace("=count", ""); });
});
</script>
</body>
Thanks for the help!
The problem can be inspected by seeing what d really is in the cx and cy function.
cx's problem: you didnt parse the date string into a Date object like you did for the slices data; cy's problem: the data item has no measurement key.
You have used both data and slices. To make the code more consistent, I fix the code of circle drawing using slices too.
linesAndDots
.selectAll(".data-circle")
.data(d=>d.values) // `d` now is the one of the two entries of `slices`,
//and we want to use the `values` array of each entry.
.enter()
.append("circle")
.attr("class", "data-circle")
.attr("r", 5)
.attr("cx", function(d) {
return xScale(d.date);
})
.attr("cy", function(d) {
return yScale(d.measurement)
});
A demo here

D3 vertical line beetween grouped chart bars spacing

The problem:
The number of groups is dynamic, and vertical line (separator) needs dynamic padding. Group width im getting with x0.rangeBand(). Is there any way to get width of space beetween two groups dynamically?
Peace of code:
.....
var slice = svg.selectAll(".chart")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function (d) {
return "translate(" + x0(d.category) + ",0)";
});
// Create rectangles of the correct width
slice.selectAll("rect")
.data(function (d) {
return d.values;
})
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function (d) {
return x1(d.rate);
})
.style("fill", function (d) {
return color(d.rate)
})
.attr("y", function (d) {
return y(0);
})
.attr("height", function (d) {
return height - y(0);
})
.on("mouseover", function (d) {
d3.select(this).style("fill", d3.rgb(color(d.rate)).darker(2));
tip.show(d);
})
.on("mouseout", function (d) {
tip.hide
d3.select(this).style("fill", color(d.rate));
})
slice.append("line")
.attr("class", "blabla")
.attr("x1", x0.rangeBand()+20)
.attr("x2", x0.rangeBand()+20)
.attr("y1", 0)
.attr("y2", height + margin.top + margin.bottom)
.style("stroke-width", 1)
.style("stroke", "#000");
.....
This is how it looks with few groups
This is how it looks with many groups
Because I see no reason why to stick to d3v3 and I can't find the d3v3 documentation easily for ordinal scales here is a d3v5 version of the code with correct placement of the vertical bars. You have to use the bandwidth and the step to calculate the position.
It is an adaptation of the example in your other question : https://bl.ocks.org/bricedev/0d95074b6d83a77dc3ad
I doubled the number of groups and it looks nice.
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1);
var x1 = d3.scaleBand();
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom()
.scale(x0)
.tickSize(0);
var yAxis = d3.axisLeft()
.scale(y);
var color = d3.scaleOrdinal()
.range(["#ca0020","#f4a582","#d5d5d5","#92c5de","#0571b0"]);
var svg = d3.select('body').append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("barchart.json", {credentials: 'same-origin'}).then(function(data) {
var categoriesNames = data.map(function(d) { return d.categorie; });
var rateNames = data[0].values.map(function(d) { return d.rate; });
x0.domain(categoriesNames);
x1.domain(rateNames).range([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(categorie) { return d3.max(categorie.values, function(d) { return d.value; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.style('opacity','0')
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style('font-weight','bold')
.text("Value");
svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');
var slice = svg.selectAll(".slice")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform",function(d) { return "translate(" + x0(d.categorie) + ",0)"; });
slice.selectAll("rect")
.data(function(d) { return d.values; })
.enter().append("rect")
.attr("width", x1.bandwidth())
.attr("x", function(d) { return x1(d.rate); })
.style("fill", function(d) { return color(d.rate) })
.attr("y", function(d) { return y(0); })
.attr("height", function(d) { return height - y(0); })
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.rgb(color(d.rate)).darker(2));
})
.on("mouseout", function(d) {
d3.select(this).style("fill", color(d.rate));
});
slice.selectAll("rect")
.transition()
.delay(function (d) {return Math.random()*1000;})
.duration(1000)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
slice.append("line")
.attr("class", "blabla")
.attr("x1", (x0.step() - x0.bandwidth())*0.5 + x0.bandwidth())
.attr("x2", (x0.step() - x0.bandwidth())*0.5 + x0.bandwidth())
.attr("y1", 0)
.attr("y2", height + margin.top + margin.bottom)
.style("stroke-width", 1)
.style("stroke", "#000");
//Legend
var legend = svg.selectAll(".legend")
.data(data[0].values.map(function(d) { return d.rate; }).reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d,i) { return "translate(0," + i * 20 + ")"; })
.style("opacity","0");
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d) { return color(d); });
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {return d; });
legend.transition().duration(500).delay(function(d,i){ return 1300 + 100 * i; }).style("opacity","1");
});
So the trick is to recalculate padding self. V3 does not have band.step() function so I added this method to get the middle of two items:
function getMiddle(x0){
var rng = x0.range();
var band = x0.rangeBand();
var padding = 0;
if(rng.length>1){
padding = (rng[1]-rng[0] - band) *0.5;
}
return band + padding;
}
And usage:
slice.append("line")
.attr("x1", getMiddle(x0))
.attr("x2", getMiddle(x0))
.attr("y1", 0)
.attr("y2", height + margin.top + margin.bottom)
.style("stroke-width", 1)
.style("stroke", "#000");

Updating graph in d3

I'm working on creating a graph that updates when a button is clicked, however when clicking the button, it seems only the axis are updating, and not the data itself.
The current version is in this plunker, I've also attached the code below:
http://plnkr.co/edit/85H6i25YPbTB0MRKtpZn?p=preview
I'm still quite new to D3 and have used a a few books and a lot of reading to get me to an ok level, but am struggling to find an answer to this specific question after trawling through many pages of the internet.
It would be amazing if anyone could give me some guidance on where I'm going wrong.
<body>
<svg width="960" height="500"></svg>
<div id="option">
<input name="updateButton" type="button" value="Click here to update the chart with results after the snap election" onclick="updateData()" />
</div>
<script type="text/javascript">
//graph 1
var svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1);
var x1 = d3.scaleBand()
.padding(0.05);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["#0087dc", "#d50000", "#FDBB30"]);
d3.csv("data.csv", function(d, i, columns) {
for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = +d[columns[i]];
return d;
}, function(error, data) {
if (error) throw error;
var keys = data.columns.slice(1);
x0.domain(data.map(function(d) {
return d.Year;
}));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(keys, function(key) {
return d[key];
});
})]).nice();
g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) {
return "translate(" + x0(d.Year) + ",0)";
})
.selectAll("rect")
.data(function(d) {
return keys.map(function(key) {
return {
key: key,
value: d[key]
};
});
})
.enter().append("rect")
.attr("x", function(d) {
return x1(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("width", x1.bandwidth())
.attr("height", function(d) {
return height - y(d.value);
})
.attr("fill", function(d) {
return z(d.key);
});
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x0));
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Seats before snap election");
var legend = g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "end")
.selectAll("g")
.data(keys.slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("x", width - 19)
.attr("width", 19)
.attr("height", 19)
.attr("fill", z);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) {
return d;
});
});
// ** Update data section (Called from the onclick)
function updateData() {
//call data
d3.csv("data_copy.csv", function(d, i, columns) {
for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = +d[columns[i]];
return d;
}, function(error, data) {
if (error) throw error;
var keys = data.columns.slice(1);
//scale range of data again
x0.domain(data.map(function(d) {
return d.Year;
}));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(keys, function(key) {
return d[key];
});
})]).nice();
var sel = svg.selectAll("g")
.data(data);
//remove
sel.exit().remove("g");
sel.enter().append("g")
.attr("transform", function(d) {
return "translate(" + x0(d.Year) + ",0)";
})
.selectAll("rect")
.data(function(d) {
return keys.map(function(key) {
return {
key: key,
value: d[key]
};
});
})
//remove
svg.selectAll("rect");
sel.exit().remove("rect");
sel.enter().append("rect")
.attr("x", function(d) {
return x1(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("width", x1.bandwidth())
.attr("height", function(d) {
return height - y(d.value);
})
.attr("fill", function(d) {
return z(d.key);
});
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x0));
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Seats after snap election");
});
}
#thedude's answer is right, but doesn't correct everything: it updates the bars, but the heights are wrong, for instance. Something to do with the inner .data join and the subsequent secondary formatting, I guess.
I checked at the same time and came up with the solution below. The core change that makes the update button update is this:
var sel = svg.selectAll("g.chartarea").selectAll("g.year").data(data);
sel.exit().remove();
sel.enter().append("g").classed("year", true);
// continuing with sel didn't update the just appended elements
// so I repeated the selection to get the new elements as well
sel = svg.selectAll("g.chartarea").selectAll("g.year");
sel.attr( // and so on
Complete script:
var svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1);
var x1 = d3.scaleBand()
.padding(0.05);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["#0087dc", "#d50000", "#FDBB30"]);
// added class to enable precise selection
g.append("g").classed("chartarea", true);
// added classes to enable precise selection
g.append("g")
.classed("axis", true)
.classed("x-axis", true);
// added classes to enable precise selection
g.append("g")
.classed("axis", true)
.classed("y-axis", true);
updateGraph("data.csv");
// ** Update data section (Called from the onclick)
function updateData() {
updateGraph("data_copy.csv");
}
function updateGraph(file) {
//call data
d3.csv(file, function(d, i, columns) {
for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = +d[columns[i]];
return d;
}, function(error, data) {
if (error) throw error;
var keys = data.columns.slice(1);
//scale range of data again
x0.domain(data.map(function(d) {
return d.Year;
}));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(keys, function(key) {
return d[key];
});
})]).nice();
var sel = svg.selectAll("g.chartarea").selectAll("g.year")
.data(data);
//remove
sel.exit().remove();
// added classes to enable precise selection
sel.enter().append("g").classed("year", true);
sel = svg.selectAll("g.chartarea").selectAll("g.year");
sel.attr("transform", function(d) {
return "translate(" + x0(d.Year) + ",0)";
})
.attr("x", function(d) {
return x1(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("width", x1.bandwidth())
.attr("height", function(d) {
return height - y(d.value);
})
.attr("fill", function(d) {
return z(d.key);
});
var parties =
sel.selectAll("rect.party")
.data(function(d) {
return keys.map(function(key) {
return {
key: key,
value: d[key]
};
});
});
parties.exit().remove();
// added classes to enable precise selection
parties.enter().append("rect").classed("party", true);
parties = sel.selectAll("rect.party");
parties.attr("x", function(d) {
return x1(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("width", x1.bandwidth())
.attr("height", function(d) {
return height - y(d.value);
})
.attr("fill", function(d) {
return z(d.key);
});
// select the axes instead of appending them here
g.selectAll("g.x-axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x0));
g.selectAll("g.y-axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Seats after snap election");
});
}
Added some further changes that may be worth a look:
Don't differentiate between initialization and update. This is exactly what D3 excels at: doing everything with the same code. In my refactored version, the code is reduced to a single updateGraph function that does both.
Use classes or identifiers to differentiate your graphical elements. There are several places where you select too much with selectAll("g") which will select nearly all elements in your chart.
Don't add stuff multiple times. For example, the axes should be added only once. In the original code, they were added twice, overlaying each other. Instead, add them once, leave them uninitialized, then later select them and set their attributes correctly.
You need to update your rect selection in your click handler like this:
...
//remove
sel = svg.selectAll("rect");
sel.exit().remove("rect");
sel.enter().append("rect")
sel.attr("x", function(d) {
return x1(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
...
You can see it in action here:
http://plnkr.co/edit/3R9lauiQQIB0IgrAk3X2?p=preview
Edit - I've updated the plunker with a working example that addresses several other issues

Trying to get values from csv into Two-sided horizontal bar-chart

I'm new to d3.js and am trying to create my own two-sided bar-chart. The chart it is based on is http://jasonneylon.wordpress.com/2013/09/05/two-sided-horizontal-barchart-using-d3-js/
I keep coming up with a blank screen whenever I run it, and am unsure as to what I am doing wrong.
var names = [];
var total = [];
var otherValue = [];
d3.text("results.csv",function(data){
var data = d3.csv.parseRows(data);
for( var a = 1;a <data.length;a++){
var names.push(data[a][0]);
var total.push(parseFloat(data[a][1]));
var otherValue.push(parseFloat(data[a][2]));
}
var labelArea = 160;
var chart,
width = 400,
bar_height = 20,
height = bar_height * (data.length);
var rightOffset = width + labelArea;
var chart = d3.select("body")
.append('svg')
.attr('class', 'chart')
.attr('width', labelArea + width + width)
.attr('height', height);
var xFrom = d3.scale.linear()
.domain([0, d3.max(otherValue)])
.range([0, width]);
var y = d3.scale.ordinal()
.domain(names)
.rangeBands([10, height]);
var yPosByIndex = function(d, index){ return y(index); }
chart.selectAll("rect.left")
.data(otherValue)
.enter().append("rect")
.attr("x", function(pos) { return width - xFrom(pos); })
.attr("y", yPosByIndex)
.attr("class", "left")
.attr("width", xFrom)
.attr("height", y.rangeBand());
chart.selectAll("text.leftscore")
.data(otherValue)
.enter().append("text")
.attr("x", function(d) { return width - xFrom(d); })
.attr("y", function(d, z){ return y(z) + y.rangeBand()/2; } )
.attr("dx", "20")
.attr("dy", ".36em")
.attr("text-anchor", "end")
.attr('class', 'leftscore')
.text(String);
chart.selectAll("text.name")
.data(names)
.enter().append("text")
.attr("x", (labelArea / 2) + width)
.attr("y", function(d){ return y(d) + y.rangeBand()/2; } )
.attr("dy", ".20em")
.attr("text-anchor", "middle")
.attr('class', 'name')
.text(String);
var xTo = d3.scale.linear()
.domain([0, d3.max(total)])
.range([0, width]);
chart.selectAll("rect.right")
.data(total)
.enter().append("rect")
.attr("x", rightOffset)
.attr("y", yPosByIndex)
.attr("class", "right")
.attr("width", xTo)
.attr("height", y.rangeBand());
});
chart.selectAll("text.score")
.data(total)
.enter().append("text")
.attr("x", function(d) { return xTo(d) + rightOffset; })
.attr("y", function(d,z){ return y(z) + y.rangeBand()/2; } )
.attr("dx", -5)
.attr("dy", ".36em")
.attr("text-anchor", "end")
.attr('class', 'score')
.text(String);
});
</script>
Using the 3 arrays was a different idea I tried after not managing to figure it out from anything i had found so far.
The csv file looks like this:
ABC,140,35
DEF,164,45
GHI,89,15
JKL,56,20
MNO,20,31
Thanks for any help

Adding new segments to a Animated Pie Chart in D3.js

I am unable to add a segment to a D3.js pie chart. I know I need to use .enter() and .append() to stage the new data -- but I am not sure how to apply that when I have the arcs grouped (which I need for the labels).
Here is my update function:
var updateChart = function(dataset) {
arcs.data(donut(dataset));
arcs.transition()
.duration(duration)
.attrTween("d", arcTween);
sliceLabel.data(donut(dataset));
sliceLabel.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; })
.style("fill-opacity", function(d) {
if (d.value === 0) { return 1e-6; }
else { return 1; }
});
};
How I setup the initial graph:
var arc = d3.svg.arc()
.innerRadius(radius * .4)
.outerRadius(radius);
var svg = d3.select("body")
.append("svg")
.append("svg")
.attr("width", width)
.attr("height", height);
var arc_grp = svg.append("g")
.attr("class", "arcGrp")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var label_group = svg.append("g")
.attr("class", "lblGroup")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var arcs = arc_grp.selectAll("path")
.data(donut(data));
arcs.enter()
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 0.8)
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.each(function(d) { return this.current = d; });
var sliceLabel = label_group.selectAll("text")
.data(donut(data));
sliceLabel.enter()
.append("text")
.attr("class", "arcLabel")
.attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; })
.attr("text-anchor", "middle")
.style("fill-opacity", function(d) {
if (d.value === 0) { return 1e-6; }
else { return 1; }
})
.text(function(d) { return d.data.label; });
Complete jsfiddle: http://jsfiddle.net/kPM5L/
What is a clean way to add the new data to the chart?
To get the transition to work smoothly, you need to add the code that you're using initially to your update function as well. Working jsfiddle here.
And some code to make SO happy -- this is what needs to be in the update function as well:
.enter()
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 0.8)
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.each(function(d) { return this.current = d; });
.enter()
.append("text")
.attr("class", "arcLabel")
.attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; })
.attr("text-anchor", "middle")
.style("fill-opacity", function(d) {
if (d.value === 0) { return 1e-6; }
else { return 1; }
})
.text(function(d) { return d.data.label; });

Categories

Resources