How to zoom elements inside chart using d3.js - javascript

I'm studying the d3.js library and i'm following this tutorial to zoom on chart.I integrated everything and works great.
This is my final program:
<!DOCTYPE html>
<meta charset="utf-8">
<title>Zoom + Pan</title>
<style>
body {
position: relative;
width: 960px;
}
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
rect {
fill: #ddd;
}
.axis path,
.axis line {
fill: none;
stroke: #fff;
shape-rendering: crispEdges;
}
.dot {
}
button {
position: absolute;
right: 30px;
top: 30px;
}
</style>
<button>Reset</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var margin = {top: 40, right: 50, bottom: 60, left: 70},
width = 1060 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom;
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var x = d3.scale.linear()
.domain([-width / 2, width / 2])
.range([0, width]);
var y = d3.scale.linear()
.domain([-height / 2, height / 2])
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(-height);;
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(-width);
var kmeans = 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 + ")")
.call(zoom);
d3.tsv("test.tsv", function(error, data) {
if (error) throw error;
data.forEach(function(d) {
d.y = +d.y;
d.x = +d.x;
});
x.domain(d3.extent(data, function(d) { return d.x; })).nice();
y.domain(d3.extent(data, function(d) { return d.y; })).nice();
kmeans.append("rect")
.attr("width", width)
.attr("height", height)
kmeans.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", 500)
.attr("y", 50)
.style("text-anchor", "end")
.text("1° Principal Component");
kmeans.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("x", -200)
.attr("y", -50)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("2° Principal Component");
var dot = kmeans.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.x); })
.attr("cy", function(d) { return y(d.y); })
.style("fill", function(d) { return color(d.cluster); });
var legend = kmeans.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(-100," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
d3.select("button").on("click", reset);
function zoomed() {
kmeans.select(".x.axis").call(xAxis);
kmeans.select(".y.axis").call(yAxis);
}
function reset() {
d3.transition().duration(750).tween("zoom", function() {
var ix = d3.interpolate(x.domain(), [-width / 2, width / 2]),
iy = d3.interpolate(y.domain(), [-height / 2, height / 2]);
return function(t) {
zoom.x(x.domain(ix(t))).y(y.domain(iy(t)));
zoomed();
};
});
}
</script>
this is test.tsv:
x y cluster
-1.0403321821456555 -0.9975352942962847 1 Cluster
-1.0404728255519613 -1.0021499065423058 1 Cluster
-1.0405312135780753 -1.0036348433263207 1 Cluster
-1.0405417259454817 -0.9883123582794969 1 Cluster
-1.0406344016908704 -0.9988259809896288 1 Cluster
-1.0406850822323188 -1.004030268612692 1 Cluster
-1.0406958447337742 -1.0065636473623911 1 Cluster
-1.0408667295862442 -1.0046081788513885 1 Cluster
-1.0408845367165218 -0.995137367062602 1 Cluster
-1.040932294864444 -0.991519347648691 1 Cluster
-1.040976952803462 -0.9833995692226501 1 Cluster
-1.0409896369345166 -0.9951495809699621 1 Cluster
-1.0410051379794218 -0.99448305469843 1 Cluster
-1.0410265061033306 -0.9951333768928067 1 Cluster
-1.0410330574179099 -0.9949308462686461 1 Cluster
-1.0410357249485886 -1.0053243527321372 1 Cluster
-1.0410491702402065 -1.006726904241483 1 Cluster
-1.041049812593761 -0.9865506278675225 1 Cluster
If someone run this code, It shows the plot, but the elments inside it remains unzoomable. Can someone tell me what's is wrong?

In your zoomed() function you only update both axis. You aren't updating the svg elements inside your graph (the svg-circle elements in your case).
If you add the following to your zoomed() function it should work:
kmeans.selectAll(".dot")
.attr("cx", function(d) { return x(d.x); })
.attr("cy", function(d) { return y(d.y); })
It's maybe worth mentioning that this will obviously not enlarge your circles. This will just adapt the domain of your graph and reposition your element accordingly.
If you are looking for a genuine zoom effect I'd suggest you use something like:
kmeans.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");

Related

How to get JSON key for d3.js chart

Here is my problem : I an array with JSON object in it ad I would like to get the keys of JSON objects to build a bar chart with it.
Here is my array :
var dataset = [{
"T1": 4.23
},
{
"T2": 45.62
},
{
"T3": 24.78
},
{
"T4": 11.41
},
{
"T5": 5.19
},
{
"T6": 5.15
},
{
"T7": 1.99
},
{
"T8": 0.93
},
{
"T9": 0.61
}
];
And here is my code to draw the chart :
let width = 500;
let heigth = 200;
let barPadding = 1;
let svg = d3.select("#containerChart")
.append("svg")
.attr("width", width)
.attr("height", heigth);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function (d, i) {
return i + (20 + barPadding);
})
.attr("y", function (d) {
return heigth - (**values of JSON object** * 2)
})
.att("width", 20)
.attr("heigth", function (d) {
return (**values of JSON object** * 2)
})
.attr("fill", "teal")
Do you have any idea of how I could do ?
Thanks in advance for your help !!
Here's the codepen and here's the code for testing on your local:
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
.bar{
fill: steelblue;
}
.bar:hover{
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// set the dimensions of the canvas
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// set the ranges
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
// define the axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
// add the SVG element
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 + ")");
// load the data
d3.json("data.json", function(error, data) {
// scale the range of the data
x.domain(data.map(function(d) { return Object.keys(d); }));
y.domain([0, d3.max(data, function(d) { return +d[Object.keys(d)]; })]);
// add axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 5)
.attr("dy", ".71em")
.style("text-anchor", "end");
// Add bar chart
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(Object.keys(d)); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(+d[Object.keys(d)]); })
.attr("height", function(d) { return height - y(+d[Object.keys(d)]); });
});
</script>
</body>
I replaced all references for the y axis with the json keys using Object.keys() and got the values for these keys to put into the x axis. You could also use for... in loop to do the same thing.
To test it locally, you will have to host both files on a local server or else you will get CORS errors. You could include a parsed inline json to avoid CORS altogether.
This one should work :
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function (d, i) {
return i + (20 + barPadding);
})
.attr("y", function (d,i) {
return heigth - d["T" + (i+1)]
})
.attr("width", 20)
.attr("heigth", function (d,i) {
return heigth - d["T" + (i+1)]
})
.attr("fill", "teal")

d3 - multiple overlaying charts with custom DOM element

I'm new to d3, but pretty familiar with the HighCharts api.
I've seen lots of examples of multiple d3 charts on the same page; but can't seem to find examples of one chart overlaying/sitting directly on top of another chart. Is this possible?
With HighCharts, you can define multiple chart types in the plotOptions config object. Is there something similar with d3? Or, how could you do this with d3?
I would effectively like to have a line graph on top of a bar chart. There will be different 'stages' according to the data, so some of the bar's could be inactive/empty.
Additionally, I need to display an indicator to show where the 'stage' is currently; and ensure that this is all responsive.
Example (rough mockup):
After researching d3 and looking for similar examples, I am thinking that maybe d3 isn't the best choice for this; maybe a custom CSS/JS/HTML solution (inside an angular app) would be better.
Any recommendations or pointers would be very appreciated.
Here's a quick mock-up started from this excellent bar chart example:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.point rect {
fill: steelblue;
}
.point circle {
fill: orange;
}
.point rect:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: orange
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 75, right: 20, bottom: 30, left: 40},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 + ")");
var data = "abcdefghijklmnopqrstuvwxyz".split("").map(function(d){
return {
letter: d,
bar: Math.random() * 10,
line: Math.random() * 10
};
})
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d3.max([d.bar, d.line]); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text");
var points = svg.selectAll(".point")
.data(data)
.enter().append("g")
.attr("class", "point");
points.append('rect')
.attr("x", function(d) { return x(d.letter); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.bar); })
.attr("height", function(d) { return height - y(d.bar); });
points.append('circle')
.attr("r", 5)
.attr("cx", function(d){ return x(d.letter) + x.rangeBand() / 2; })
.attr("cy", function(d){ return y(d.line)});
var line = d3.svg.line()
.x(function(d) { return x(d.letter) + x.rangeBand() / 2; })
.y(function(d) { return y(d.line); });
svg.append("path")
.attr("class", "line")
.datum(data)
.attr("d", line);
var indicator = svg.append("g")
.attr("r", 5)
.attr("transform", "translate(" + (x("q") + x.rangeBand() / 2) + "," + -20 + ")");
indicator.append("circle")
.attr("r", 40)
.style("fill", "red");
indicator.append("text")
.text("!")
.style("fill", "white")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.style("font-size", 70);
indicator.append("line")
.attr("y1", 20)
.attr("y2", height + 20)
.attr("x1", 0)
.attr("x2", 0)
.style("stroke", "red")
.style("stroke-width", "4px");
</script>
New Solution Based on Comments
Given your input data, here's a new example. I went a bit overboard here, so please ask question on any confusing bits. I tried to comment it out:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
rect {
fill: steelblue;
}
circle {
fill: orange;
}
rect:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: orange
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {
top: 75,
right: 20,
bottom: 30,
left: 40
},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 + ")");
// here's your data
var data =
{
'point1': [{
'value': 50
}, {
'value': 100
}, {
'value': 100
}, {
'value': 150
}],
'point2': [{
'value': 25
}, {
'value': 40
}, {
'value': 60
}],
'point3': [{
'value': 25
}]
};
// d3ify your data
// d3 likes arrays of objects, you have an object of objects
// so first make it an array
var barData = d3.entries(data);
// set x domain
x.domain(barData.map(function(d){ return d.key }));
// create lineData
var lineData = [];
barData.forEach(function(d0, i){
d0.mean = d3.mean(d0.value, function(d1){ return d1.value });
d0.max = d3.max(d0.value, function(d1){ return d1.value});
var N = d0.value.length,
// this is an inner scale
// that represents each bar
s = d3.scale.linear().range([
x(d0.key) + (x.rangeBand() / N) / 2,
x(d0.key) + x.rangeBand()
]).domain([
0, N
])
d0.value.forEach(function(d1, j){
lineData.push({
x: s(j), // this is the pixel position of x, it's jittered on the bar
y: d1.value // this is the user position of y
})
});
});
// set y domain
y.domain([0, d3.max(barData, function(d) {
return d.max;
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text");
// draw bars
var bars = svg.selectAll(".bar")
.data(barData)
.enter()
.append('rect')
.attr('class', 'bar')
.attr("x", function(d) {
return x(d.key);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.mean);
})
.attr("height", function(d) {
return height - y(d.mean);
});
// add points
var points = svg.selectAll('point')
.data(lineData)
.enter()
.append('circle')
.attr('class', 'point')
.attr("r", 5)
.attr("cx", function(d) {
return d.x; // already pixel position
})
.attr("cy", function(d) {
return y(d.y)
});
var line = d3.svg.line()
.x(function(d) {
return d.x; // already pixel position
})
.y(function(d) {
return y(d.y);
});
svg.append("path")
.attr("class", "line")
.datum(lineData)
.attr("d", line);
var indicator = svg.append("g")
.attr("transform", "translate(" + (x("point2") + x.rangeBand() / 2) + "," + -20 + ")");
indicator.append("circle")
.attr("r", 40)
.style("fill", "red");
indicator.append("text")
.text("!")
.style("fill", "white")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.style("font-size", 70);
indicator.append("line")
.attr("y1", 20)
.attr("y2", height + 20)
.attr("x1", 0)
.attr("x2", 0)
.style("stroke", "red")
.style("stroke-width", "4px");
</script>
Happy d3ing!

error updating my barchart using d3

I came through this problem just when I though I had d3 undercontrol... I can't find out why my code is crashing when I update my barChart using an empty array of data...
here is my code:
<!DOCTYPE html>
<html>
<head>
<title>Simple tables in D3</title>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<style type="text/css">
.chart rect {
fill: steelblue;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.axis {
font: 10px sans-serif;
}
</style>
</head>
<body>
<svg class="chart">
</svg>
<script>
var scanCounters = JSON.parse("[{\"scan\":\"111\",\"repetition\":3},{\"scan\":\"222\",\"repetition\":2},{\"scan\":\"333\",\"repetition\":4},{\"scan\":\"123\",\"repetition\":2},{\"scan\":\"456\",\"repetition\":1},{\"scan\":\"789\",\"repetition\":1}]");
var scanCounters2 = JSON.parse("[{\"scan\":\"111\",\"repetition\":8},{\"scan\":\"222\",\"repetition\":6},{\"scan\":\"333\",\"repetition\":5},{\"scan\":\"123\",\"repetition\":3}]");
var scanCounters3 = JSON.parse("[]");
var columns = ["scan", "repetition"];
var margin = {top: 20, right: 30, bottom: 30, left: 40};
var width = 960;
var height = 500;
var innerWidth = width - margin.left - margin.right;
var innerHeight = height - margin.top - margin.bottom;
var bcScan = barChart(600, 400, "chart");
bcScan.update(scanCounters);
setTimeout(function(){
bcScan.update(scanCounters2);
}, 1000);
setTimeout(function(){
bcScan.update(scanCounters3);
}, 2000);
function barChart(w, h, node) {
var bC = {};
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = w - margin.left - margin.right,
height = h - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("." + node)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "y axis")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -40)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
bC.update = function(data) {
x.domain(data.map(function (d) {
return d[Object.keys(d)[0]];
}));
y.domain([0, d3.max(data, function (d) {
return d[Object.keys(d)[1]];
})]);
svg.select(".x.axis")
.transition()
.duration(300).call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
svg.select(".y.axis")
.transition()
.duration(300)
.call(yAxis);
var bars = svg.selectAll(".bar").data(data, function (d) {
return d[Object.keys(d)[0]];
});
bars.exit()
.transition()
.duration(300)
.attr("y", y(0))
.attr("height", height - y(0))
.style('fill-opacity', 1e-6)
.remove();
bars.enter()
.append("rect")
.attr("class", "bar")
.attr("y", y(0))
.attr("height", height - y(0));
var trans = bars.transition().duration(300).attr("x", function (d) {
return x(d[Object.keys(d)[0]]);
});
trans.attr("width", x.rangeBand())
.attr("y", function (d) {
return y(d[Object.keys(d)[1]]);
})
.attr("height", function (d) {
return height - y(d[Object.keys(d)[1]]);
});
};
return bC;
}
</script>
</body>
</html>
The problem is when I call bcScan.update(scanCounters3); where scanCounters3 is an empty array.
This is the error message:
Error: Invalid value for attribute height="NaN"
Error: Invalid value for attribute y="NaN"
QUESTON: Could someone help me to understand why it is crashing?
thanks
#EthanJewett, is correct, if you look at your y.domain() on each iteration you get:
[0, 4]
[0, 8]
[0, NaN]
So on your last iteration, your exit transition is doing math on an NaN:
.attr("y", y(0))
.attr("height", height - y(0))
I would just drop those lines altogether and make the exit transition:
bars.exit()
.transition()
.duration(300)
.style('fill-opacity', 1e-6)
.remove();

D3 chart not showing up

Problem
Having trouble getting my chart in D3 to show up. Getting an Uncaught SyntaxError: Unexpected end of input with my scripts.js
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<script src="assets/js/scripts.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
</body>
</html>
scripts.js
$(function() {
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.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
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.csv("assets/data/data.csv", function(error, data) {
var ageNames = d3.keys(data[0]).filter(function(key) { return key !== "State"; });
data.forEach(function(d) {
d.ages = ageNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.State; }));
x1.domain(ageNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.ages, 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")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Population");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
style.css
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: blue;
}
.x.axis path {
display: none;
}
I found that I got the same error when pasting your code directly into JSFiddle. It went away when I properly closed the main function. For reference, here's your code in live snippet form with the extra closing braces:
var csvData = [
{"State":0, "Age":31, "Names":["Austin"]},
{"State":0, "Age":31, "Names":["Austin"]},
{"State":0, "Age":31, "Names":["Austin"]},
];
$(function() {
$("#placeholder").remove();
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.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
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 + ")");
var data = csvData;
var ageNames = d3.keys(data[0]).filter(function(key) { return key !== "State"; });
data.forEach(function(d) {
d.ages = ageNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.State; }));
x1.domain(ageNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.ages, 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")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Population");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: blue;
}
.x.axis path {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
On the d3.csv("csv...") call - try adding another }); to the end of it.
I tried your code out on JSFiddle, and that seemed to resolve the syntax error.
EDIT: My bad. Left in a hurry. Here's the fiddle:
http://jsfiddle.net/strsjpxa/
I later noticed it made more sense to close the function at the end.

Hyperlink chart bars using D3

I am using the D3 toolkit to create a stacked bar graph of some data. I want link the bars to a separate page with more detail about the underlying data for that bar. I can't figure out how to turn the bars into hyperlinks. The code below is based on a demo that I have been tinkering with. When I inspect the code in Google Chrome it looks correct but the bars aren't clickable. Tremendous thanks in advance for ideas and suggestions on this!
Source, attempting to add link to Google to each bar:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="d3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
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.csv("./data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; }));
data.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d) { return d.State; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Population");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("xlink:href", function(d) { return "http://www.google.com"; })
.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
state.append("a")
.attr("xlink:href", "http://www.google.com")
;
var legend = svg.selectAll(".legend")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
</script>
I'm not sure that you can add "a" html mark in SVG.
But you can add an event on each bar segment:
state.on('click',function(d){
... some great code ...
})

Categories

Resources