How to increase xaxis in d3js v4 bar chart? - javascript

I'm trying to learn how to code with the d3.js. I am trying to make a simple bar graph with this json file. I got stuck trying to format the xaxis in the file. I've tried looking at the d3.js API and I am still lost. I would be very grateful for any help.
Here is the result screenshot
This image is for shorter xaxis points
This output looks good
This output results when more data points in xaxis
Can anyone suggest me how to increase the xaxis length based on data point coiming to xaxis
Here is my code
.bar {
fill: #F39473;
}
.highlight {
fill: orange;
}
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<svg width="900" height="500"></svg>
<script>
var svg = d3.select("svg"),
top= 20, right= 20, bottom= 50, left= 70,
margin = 200,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin;
var x = d3.scaleBand().range([0, width]).padding(0.4),
y = d3.scaleLinear().range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
d3.json("data.php", function(error, data) {
data.forEach(function(d) {
d.date = (d.date);
d.count = +d.count;
})
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.count; })]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.append("text")
.attr("y", height - 250)
.attr("x", width - 100)
.attr("text-anchor", "middle")
.attr("stroke", "black")
.text("date");
g.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("count");
g.append("g")
.call(d3.axisLeft(y).tickFormat(function(d){
return d;
}).ticks(10))
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.on("mouseover", onMouseOver) //Add listener for the mouseover event
.on("mouseout", onMouseOut) //Add listener for the mouseout event
.attr("x", function(d) { return x(d.date); })
.attr("y", function(d) { return y(d.count); })
.attr("width", x.bandwidth())
.transition()
.ease(d3.easeLinear)
.duration(400)
.delay(function (d, i) {
return i * 50;
})
.attr("height", function(d) { return height - y(d.count); });
});
//mouseover event handler function
function onMouseOver(d, i) {
d3.select(this).attr('class', 'highlight');
d3.select(this)
.transition() // adds animation
.duration(400)
.attr('width', x.bandwidth() + 5)
.attr("y", function(d) { return y(d.count) - 10; })
.attr("height", function(d) { return height - y(d.count) + 10; });
g.append("text")
.attr('class', 'val')
.attr('x', function() {
return x(d.date);
})
.attr('y', function() {
return y(d.count) - 15;
})
.text(function() {
return [ +d.date, +d.count]; // Value of the text
});
}
//mouseout event handler function
function onMouseOut(d, i) {
// use the text label class to remove label on mouseout
d3.select(this).attr('class', 'bar');
d3.select(this)
.transition() // adds animation
.duration(400)
.attr('width', x.bandwidth())
.attr("y", function(d) { return y(d.count); })
.attr("height", function(d) { return height - y(d.count); });
d3.selectAll('.val')
.remove()
}
</script>
</body>
</html>

I would use d3.nest() and make a rollup out of the key value you'd want to count (d.date in this case) and use this count value in your width variable.
Here's a plunker I made using this method.

Related

text labels are wrong in grouped bar chart d3js

I am newbie in d3js, I do not know why all labels in the bar are wrong.
My code and captures are shown as below, then you can see that all labels are different from my data.
Anyone know what's going on in my text label section?
// set the dimensions and margins of the graph
var margin = { top: 10, right: 30, bottom: 40, left: 50 },
width = 700 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
const dataUrl = "https://raw.githubusercontent.com/yushinglui/IV/main/time_distance_status_v2.csv"
//fetch the data
d3.csv(dataUrl)
.then((data) => {
// append the svg object to the body of the page
var svg = d3.select("#graph-2")
.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 + ")")
// List of subgroups = header of the csv files = soil condition here
var subgroups = data.columns.slice(1)
// List of groups = species here = value of the first column called group -> I show them on the X axis
var groups = d3.map(data, function (d) { return (d.startTime) })
// Add X axis
var x = d3.scaleBand()
.domain(groups)
.range([0, width])
.padding([0.2])
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSize(0));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 20])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Another scale for subgroup position?
var xSubgroup = d3.scaleBand()
.domain(subgroups)
.range([0, x.bandwidth()])
.padding([0.05])
// color palette = one color per subgroup
var color = d3.scaleOrdinal()
.domain(subgroups)
.range(['#98abc5', '#8a89a6'])
// Show the bars
svg.append("g")
.selectAll("g")
// Enter in data = loop group per group
.data(data)
.enter()
.append("g")
.attr("transform", function (d) { return "translate(" + x(d.startTime) + ",0)"; })
.selectAll("rect")
.data(function (d) { return subgroups.map(function (key) { return { key: key, value: d[key] }; }); })
.enter()
.append("rect")
.attr("x", function (d) { return xSubgroup(d.key); })
.attr("y", function (d) { return y(d.value); })
.attr("width", xSubgroup.bandwidth())
.attr("height", function (d) { return height - y(d.value); })
.attr("fill", function (d) { return color(d.key); })
// mouseover and mouseout animation
.on("mouseover", function (d) {
d3.select(this).style("fill", d3.rgb(color(d.key)).darker(2))
})
.on("mouseout", function (d) {
d3.select(this).style("fill", function (d) { return color(d.key); })
})
//axis labels
svg.append('text')
.attr('x', - (height / 2))
.attr('y', width - 650)
.attr('transform', 'rotate(-90)')
.attr('text-anchor', 'middle')
.style("font-size", "17px")
.text('Average Distance');
svg.append('text')
.attr('x', 300)
.attr('y', width - 240)
.attr('transform', 'rotate()')
.attr('text-anchor', 'middle')
.style("font-size", "17px")
.text('Start Time');
// legend
svg.append("circle").attr("cx", 200).attr("cy", 20).attr("r", 6).style("fill", "#98abc5")
svg.append("circle").attr("cx", 300).attr("cy", 20).attr("r", 6).style("fill", "#8a89a6")
svg.append("text").attr("x", 220).attr("y", 20).text("Present").style("font-size", "15px").attr("alignment-baseline", "middle")
svg.append("text").attr("x", 320).attr("y", 20).text("Absent").style("font-size", "15px").attr("alignment-baseline", "middle")
//text labels on bars -- all labels wrong!!
svg.append("g")
.selectAll("g")
// Enter in data = loop group per group
.data(data)
.enter()
.append("g")
.attr("transform", function (d) { return "translate(" + x(d.startTime) + ",0)"; })
.selectAll("text")
.data(function (d) { return subgroups.map(function (key) { return { key: key, value: d[key] }; }); })
.enter()
.append("text")
.text(function (d) { return y(d.value); })
.attr("font-family", "sans-serif")
.attr("font-size", "12px")
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("x", function (d) { return xSubgroup(d.key); })
.attr("y", function (d) { return y(d.value) + 10; })
});
My reference website:
http://plnkr.co/edit/9lAiAXwet1bCOYL58lWN?p=preview&preview
https://bl.ocks.org/bricedev/0d95074b6d83a77dc3ad
Your issue is that when you're appending the text, you inadvertently called the y function, which is used to get the y-location on where to insert the text. The numbers you're getting are actually y-location values, which seems completely random.
.text(function (d) { return y(d.value); }) // here is the issue
Change it to
.text(function (d) { return d.value; })
and it should work!

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

Apply D3 tooltip to Donut Multiples

I have code like this that creates multiple D3 donut multiples.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
svg {
padding: 10px 0 0 10px;
}
.arc {
stroke: #fff;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var radius = 74,
padding = 10;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - 30);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
d3.csv("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; }));
data.forEach(function(d) {
d.ages = color.domain().map(function(name) {
return {name: name, population: +d[name]};
});
});
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", radius * 2)
.attr("height", radius * 2)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d; });
var svg = d3.select("body").selectAll(".pie")
.data(data)
.enter().append("svg")
.attr("class", "pie")
.attr("width", radius * 2)
.attr("height", radius * 2)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
svg.selectAll(".arc")
.data(function(d) { return pie(d.ages); })
.enter().append("path")
.attr("class", "arc")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.name); });
svg.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.State; });
});
</script>
I am looking for a way to implement the D3 tooltip so that I can see the exact data of each chunk of the donut when I put my cursor over it. I understand that there are other examples of the tooltip on here but none of them have worked with the donut multiples example.
Here is some example data
State,Under 5 Years,5 to 13 Years,14 to 17 Years,18 to 24 Years,25 to 44 Years,45 to 64 Years,65 Years and Over
AL,310504,552339,259034,450818,1231572,1215966,641667
AK,52083,85640,42153,74257,198724,183159,50277
AZ,515910,828669,362642,601943,1804762,1523681,862573
AR,202070,343207,157204,264160,754420,727124,407205
CA,2704659,4499890,2159981,3853788,10604510,8819342,4114496
CO,358280,587154,261701,466194,1464939,1290094,511094
CT,211637,403658,196918,325110,916955,968967,478007
DE,59319,99496,47414,84464,230183,230528,121688
DC,36352,50439,25225,75569,193557,140043,70648
FL,1140516,1938695,925060,1607297,4782119,4746856,3187797
GA,740521,1250460,557860,919876,2846985,2389018,981024
HI,87207,134025,64011,124834,356237,331817,190067
ID,121746,201192,89702,147606,406247,375173,182150
IL,894368,1558919,725973,1311479,3596343,3239173,1575308
IN,443089,780199,361393,605863,1724528,1647881,813839
IA,201321,345409,165883,306398,750505,788485,444554
KS,202529,342134,155822,293114,728166,713663,366706
KY,284601,493536,229927,381394,1179637,1134283,565867
LA,310716,542341,254916,471275,1162463,1128771,540314
ME,71459,133656,69752,112682,331809,397911,199187
MD,371787,651923,316873,543470,1556225,1513754,679565
The D3 doc of this can be found at
http://bl.ocks.org/mbostock/3888852
I'm not sure what you're referring to when you say "the D3 tooltip", because d3 doesn't have any built-in tooltip functionality. That said, there are some good third-party plugins out there for doing tooltips in d3.
d3-tip is one that would work well for what you're trying to do.
You can create a tooltip function to display your population data for each arc like this:
var tip = d3.tip()
.attr('class', 'd3-tip')
.html(function(d) { return d.data.population; })
.direction('s');
Then you can call the function on your svg selection:
svg.call(tip);
Finally, you can use mouse event listeners on your arcs to show and hide the tooltip:
svg.selectAll(".arc")
.data(function(d) { return pie(d.ages); })
.enter().append("path")
.attr("class", "arc")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.name); })
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
Here's a PLUNK with a working example.
You can also check up on the d3-tip documentation here.
I too got the same TypeError while debugging my javascript code using d3js while adding tooltip for my barcharts.
bars.append('rect')
.attr('y', maxHeight)
.attr('height', 0)
.attr('width', function (d) { return x.rangeBand(d) - 1; })
.attr('class', 'bar')
.transition().duration(1500)
.attr('y', function (d, i) { return y(d.y); })
.attr('height', function (d, i) { return maxHeight - y(d.y); });
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
In the above code i used ".on('mouseover', tip.show)" on "bars.append()".This is wrong usage.
Later, i found that ".on('mouseover', tip.show)" should be applied on function ".select('rect') or .selectall('rect')".
Now below snippet is working correctly for my application.
bars.append('rect')
.attr('y', maxHeight)
.attr('height', 0)
.attr('width', function (d) { return x.rangeBand(d) - 1; })
.attr('class', 'bar')
.transition().duration(1500)
.attr('y', function (d, i) { return y(d.y); })
.attr('height', function (d, i) { return maxHeight - y(d.y); });
bars.select('rect')
.on('mouseover', tip.show)
.on('mouseout', tip.hide);

D3.js horizontal histogram

I'm trying to design a horizontal histogram and I've only encountered the vertical histogram chart by Mike Bostock. As mentioned here, the slightly hacky solution is to do a transform rotate on the element containing the histogram. Either that or don't use d3.layout.histogram() and write out the code manually. Is there a way in D3 to do something along the lines of
d3.layout.histogram().orient("left")
You can simply take care of it at render time without any transforms or recalculating. Simply treat d.x as y-position, d.dy as width instead of height.
Swapping between x and y might seem inappropriate, but it's totally reasonable. There are even examples of radial charts drawn this way too, using the x values to derive the angle of the bar and the y value as distance from the center. That's the neat thing about d3 layouts; they don't commit you to a representation or coordinate system —— they just calculate relationships.
For both verti & hori, you can use those 2 "well-rounded" instances i use pretty much all the time with a sexy transition.
But there is nonetheless a transform method mobilized there.
Vertical:
example
<svg width="550" height="250"></svg>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 60},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.tsv("data.txt", function(d) {
d.value = +d.value;
return d;
}, function(error, data) {
if (error) throw error;
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "rotate(-45)")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.attr("x", x(data[0].date))
.style("text-anchor", "end")
.attr("dx", "-1.2em")
.attr("dy", ".01em")
.attr("transform", "rotate(-45)" );
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10, "r"))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Frequency");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
//.attr("x", function(d) { return x(d.date); })
//.attr("y", function(d) { return y(d.value); })
.attr('x', function(d) { return getX(d);})
.attr('y', height) /* function(d) { return height; }}) */
.attr("width", x.bandwidth())
.transition()
.duration(500)
.delay(function(d, i) { return i * 35;})
.ease(d3.easeElasticOut)
.attr('y', function(d) { return getY(d); })
.attr('height', function(d) { return height - getY(d);});
//.attr("height", function(d) { return height - y(d.value); });
g.selectAll(".ba")
.data(data)
.enter().append("text")
.attr("x", function(d) { return x(d.date); })
.attr("y", function(d) { return y(d.value); })
.attr("dy", ".90em")
.attr("dx", ".35em")
.attr("class","label_barre")
.text(function(d) { return d.value;});
function type(d) {
d.value = +d.value;
return d;
}
function getName(d) {
return d.date;
}
function getValue(d) {
return d.value;
}
function getX(d) {
return x(d.date);
}
function getY(d) {
return y(d.value);
}
});
</script>
Horizontal:example
<svg width="560" height="400"></svg>
<script>
var width = 560,
height = 400;
var margin = {top: 20, right: 15, bottom: 30, left: 80},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
var x = d3.scaleLinear().rangeRound([0, height], .05);
var y = d3.scaleBand().rangeRound([height, 0]);
var q = d3.select("svg").append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.tsv("Data.txt", function(error, data) {
data.sort(function(a, b) { return a.value - b.value; });
//data.sort(function(a, b) { return a.date - b.date; });
data.forEach(function(d) {
d.date = d.date;
d.value = +d.value;
});
x.domain([0, d3.max(data, function(d) { return d.value; })]);
y.domain(data.map(function(d) { return d.date; })).padding(0.1);
q.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y));
q.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(10))
.append("text")
.attr("transform", "rotate(0)")
.attr("y", -12)
.attr("x", +211)
.attr("dy", "0.71em")
.attr("text-anchor", "middle")
.text("Familles");
q.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
//.attr("x", function(d) { return x(d.date); })
//.attr("y", function(d) { return y(d.value); })
.attr('y', function(d) {return getX(d);})
.attr('x', 0) /* function(d) { return height;}) */
.attr("height", y.bandwidth())
.transition()
.duration(500)
.delay(function(d, i) { return i * 40;})
.ease(d3.easeElasticOut)
.attr('y', function(d) { return getX(d);})
// .attr('width', function(d) { return width - getY(d);});
.attr("width", function(d) { return x(d.value); });
q.selectAll(".ba")
.data(data)
.enter().append("text")
.attr("x", function(d) { return x(d.value); })
.attr("y", function(d) { return y(d.date); })
.attr("dy", "1.4em")
.attr("dx", "-1.1em")
.attr("class","label")
.text(function(d) { return d.value;});
//function type(d) {
//d.value = +d.value;
//return d;
//}
//function getName(d) {
//return d.date;
//}
function getValue(d) {
return d.value;
}
function getX(d) {
return y(d.date);
}
//function getY(d) {
//return x(d.value);
//}
});

Categories

Resources