I'm trying to get an understanding of transitions in bar charts using D3. What I'm doing now is updating the chart between two different data sets. I have a transition included but it's starting from the the bottom of the axis rather than transitioning between the two. My goal is to have it transition between the two and later change the colors. I'm using this helpful example for understanding updated data (my snippet is not much different). Thank you for taking a look.
var bothData = [
{
"year": "2014",
"product": "Books & DVDs",
"purchase": "0.5"
},
{
"year": "2002",
"product": "Books & DVDs",
"purchase": "10"
},
{
"year": "2014",
"product": "Beer & Wine",
"purchase": "7"
},
{
"year": "2002",
"product": "Beer & Wine",
"purchase": "3"
},
{
"year": "2014",
"product": "Food",
"purchase": "12"
},
{
"year": "2002",
"product": "Food",
"purchase": "12"
},
{
"year": "2014",
"product": "Home Supplies",
"purchase": "7"
},
{
"year": "2002",
"product": "Home Supplies",
"purchase": "6"
}
];
var data2002 = [];
var data2014 = [];
for(var i = 0; i < bothData.length; i++){
if(bothData[i]["year"] === "2002"){
data2002.push(bothData[i]);
}else{
data2014.push(bothData[i]);
}
}
function change(value){
if(value === '2002'){
update(data2002);
}else if(value === '2014'){
update(data2014);
}
}
function update(data){
xChart.domain(data.map(function(d){ return d.product; }) );
yChart.domain( [0, d3.max(data, function(d){ return + d.purchase; })] );
var barWidth = width / data.length;
var bars = chart.selectAll(".bar")
.remove()
.exit()
.data(data, function(d){ return d.purchase; })
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d, i){ return i * barWidth + 1 })
.attr("y",500)
.attr("height",0)
.attr("width", barWidth - 5)
.each(function(d){
if(d.year === "2014"){
d3.select(this)
.style('fill','#ea5454');
}else{
d3.select(this)
.style('fill','#4e97c4');
};
});
bars.transition()
.duration(600)
.ease(d3.easeLinear)
.attr('y', function(d){ return yChart(d.purchase); })
.attr('height', function(d){ return height - yChart(d.purchase); });
chart.select('.y').call(yAxis);
chart.select('.xAxis')
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d){
return "rotate(-65)";
});
}
var margin = {top: 20, right: 20, bottom: 95, left: 50};
var width = 400;
var height = 500;
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xChart = d3.scaleBand()
.range([0, width]);
var yChart = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom(xChart);
var yAxis = d3.axisLeft(yChart);
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
chart.append("g")
.attr("class", "xAxis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d){
return "rotate(-65)";
});
chart.append("text")
.attr("transform", "translate(-35," + (height+margin.bottom)/2 + ") rotate(-90)")
.text("Purchases");
chart.append("text")
.attr("transform", "translate(" + (width/2) + "," + (height + margin.bottom - 5) + ")")
.text("Products");
update(data2002);
You have the right idea...you want to achieve object constancy as described here by Mike Bostock. The key for your constancy should be the "product" from your data (not "purchase").
In your update function, define bars like this:
var bars = chart.selectAll(".bar")
.data(data, function(d){ return d.product; })
then separate the .enter, .exit and .transition functions:
bars.exit()
.remove()
bars.enter()
....
bars.transition()
You have some strange stuff going on in your .enter function - like setting bar height to zero. So I modified your .enter and .transition functions like this:
bars.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d, i){return i * barWidth + 1 })
.attr("y",function(d){ return yChart(d.purchase); })
.attr("height",function(d){ return height - yChart(d.purchase); })
.attr("width", barWidth - 5)
.attr('fill', function(d){
if(d.year === "2014"){
return'#ea5454'
}else{
return'#4e97c4'
}
})
bars.transition()
.duration(600)
.ease(d3.easeLinear)
.attr('y', function(d){ return yChart(d.purchase); })
.attr('height', function(d){ return height - yChart(d.purchase); })
.style('fill', function(d){
if(d.year === "2014"){
return '#ea5454'
} else {
return '#4e97c4'
}
})
Here is a working jsfiddle: https://jsfiddle.net/genestd/asoLph2w/
Note the buttons on top to achieve the transition.
Related
I'm trying to display values on the top of line graph using d3 js. How can I append values to the path of the line graph?
And How can I display month-year as X-axis ticks like May'18, June'19?
I tried using the below code, but the values are not displaying.
lineSvg.selectAll("path")
.data(data)
.enter().append("text")
.attr("class", "line")
.attr("text-anchor", "middle")
.attr("x", function(d) { return 10; })
.attr("y", function(d) { return yscale(d.value) - 5; })
.text(function(d) { return d.value; });
Here is my complete code:
var data = [{
"date": "2018-1",
"value": 40.13,
"status": 1
}, {
"date": "2018-2",
"value": 45.88,
"status": 1
}, {
"date": "2018-3",
"value": 50.89,
"status": 1
}, {
"date": "2018-4",
"value": 55.87,
"status": 1
}, {
"date": "2018-5",
"value": 88.54,
"status": 1
}, {
"date": "2018-6",
"value": 74.41,
"status": 1
}, {
"date": "2018-7",
"value": 98.56,
"status": 1
}, {
"date": "2018-8",
"value": 81.05,
"status": 1
}, {
"date": "2018-9",
"value": 58.13,
"status": 1
}, {
"date": "2018-10",
"value": 95.86,
"status": 1
}, {
"date": "2018-11",
"value": 78.13,
"status": 1
}, {
"date": "2018-12",
"value": 98.86,
"status": 1
}, {
"date": "2019-1",
"value": 105.86,
"status": 0
}, {
"date": "2019-2",
"value": 110.86,
"status": 0
}];
/* Monday 2012 */
var data1 = data
var dateformat = "%Y-%m"
drawTimeSeriesGraph(data1, dateformat);
/*
Tooltip from: http://bl.ocks.org/d3noob/6eb506b129f585ce5c8a
line graph from here: http://www.d3noob.org/2012/12/starting-with-basic-d3-line-graph.html
*/
function drawTimeSeriesGraph(data, dateformat) {
//Set bounds for red dots
var lbound = 0.045,
ubound = 0.075;
// Set the dimensions of the canvas / graph
var margin = {
top: 50,
right: 150,
bottom: 50,
left: 50
},
width = 900 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format(dateformat).parse,
formatDate = d3.time.format(dateformat),
bisectDate = d3.bisector(function(d) {
return d.date;
}).left;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(10);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(10);
// Define the line
var valueline = d3.svg.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.value);
}).interpolate("linear");
var full_date = new Date();
var day = full_date.getDay(); //returns 0 - 6
var month = full_date.getMonth() + 1; //returns 0 - 11
var year = full_date.getFullYear(); //returns 4 digit year ex: 2000
var my = year + "-" + month;
//alert(my);
// Adds the svg canvas
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 lineSvg = svg.append("g");
var focus = svg.append("g")
.style("display", "none");
// Get the data
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value = +d.value;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.value;
})]);
//Use below if instead you want to define the y limits:
//y.domain([0, 0.11]);
// Add the valueline path.
var lineGraph2 = lineSvg.append("path")
.attr("class", "line")
.attr("d", valueline(data.filter(function(d) {
return d.status > 0;
})))
.attr("stroke", "blue")
.attr("stroke-width", 2)
.attr("fill", "none");
var lineGraph1 = lineSvg.append("path")
.attr("class", "line")
.attr("d", valueline(data.slice(-3)))
.attr("stroke", "red")
.attr("stroke-width", 2)
.attr("fill", "none");
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// append the x line
focus.append("line")
.attr("class", "x")
.style("stroke", "blue")
.style("stroke-dasharray", "3,3")
.style("opacity", 0.5)
.attr("y1", 0)
.attr("y2", height);
// append the y line
focus.append("line")
.attr("class", "y")
.style("stroke", "blue")
.style("stroke-dasharray", "3,3")
.style("opacity", 0.5)
.attr("x1", width)
.attr("x2", width);
// append the circle at the intersection
focus.append("circle")
.attr("class", "y")
.style("fill", "none")
.style("stroke", "blue")
.attr("r", 4);
// place the value at the intersection
focus.append("text")
.attr("class", "y1")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "-.3em");
focus.append("text")
.attr("class", "y2")
.attr("dx", 8)
.attr("dy", "-.3em");
// place the date at the intersection
focus.append("text")
.attr("class", "y3")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "1em");
focus.append("text")
.attr("class", "y4")
.attr("dx", 8)
.attr("dy", "1em");
// append the rectangle to capture mouse
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.on("mouseover", function() {
focus.style("display", null);
})
.on("mouseout", function() {
focus.style("display", "none");
})
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus.select("circle.y")
.attr("transform", "translate(" + x(d.date) + "," + y(d.value) + ")");
focus.select("text.y1")
.attr("transform", "translate(" + x(d.date) + "," + y(d.value) + ")")
.text(d.value.toFixed(2));
focus.select("text.y2")
.attr("transform", "translate(" + x(d.date) + "," + y(d.value) + ")")
.text(d.value.toFixed(2));
focus.select("text.y3")
.attr("transform", "translate(" + x(d.date) + "," + y(d.value) + ")")
.text(formatDate(d.date));
focus.select("text.y4")
.attr("transform", "translate(" + x(d.date) + "," + y(d.value) + ")")
.text(formatDate(d.date));
focus.select(".x")
.attr("transform", "translate(" + x(d.date) + "," + y(d.value) + ")")
.attr("y2", height - y(d.value));
focus.select(".y")
.attr("transform", "translate(" + width * -1 + "," + y(d.value) + ")")
.attr("x2", width + width);
};
svg.append("text")
.attr("x", (width / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("text-decoration", "underline")
};
I'm trying to get line graph like this:
For the axis you have to set the tickFormat.
xAxis.tickFormat(d3.time.format("%b'%y"));
or if you want full month name
xAxis.tickFormat(d3.time.format("%B'%y"));
For the text just add them like you do with circles or rects
svg.selectAll(".val").data(data)
.enter()
.append('text')
.attr("x", d => x(d.date))
.attr("y", d => y(d.value))
.attr("dy", "-0.5em")
.attr("text-anchor", "middle")
.text(d => d.value);
Okay I am trying to make a bar chart. This is the Javascript I have.
var data = [
{
"date": "1459468800000", // 1 April 2016
"values": [{
"name": "US",
"value": 72580613,
"domainname": "com"
}, {
"name": "US",
"value": 161645,
"domainname": "nl"
}]
}, {
"date": "1467331200000", // 1 Juli 2016
"values": [{
"name": "US",
"value": 73129243,
"domainname": "com"
}, {
"name": "US",
"value": 166152,
"domainname": "nl"
}]
}
]
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var tooltip = d3.select("body").append("div").attr("class");
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var colours = d3.scaleOrdinal()
.range(["#6F257F", "#CA0D59"]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(data.map(function(d) { return d.values.domainname; }));
y.domain([0, d3.max(data, function(d) { return d.values.value; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(25).tickFormat(function(d) { return parseInt(d / 1000) + "K"; }).tickSizeInner([-width]))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.attr("fill", "#5D6971")
.text("Aantal domeinen");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("x", function(d) { return x(d.values.domainname); })
.attr("y", function(d) { return y(d.values.value); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.values.value); })
.attr("fill", function(d) { return colours(d.values.domainname); })
As you can see I have the data in an array. In the data I have 2 different dates.
For now I only want to show the first date in the bar chart but both domains (com & nl as bars (In the future I will ad more domains to the dates). (If you are interested : The idea is to make a dropdown on the page so the user can select another date and the bars will update).
What I have now is that I can see the axises on my screen so that is working good. But I got this error :
So D3 is expecting a number but it doesn't get one on the Y axis and the heigth as I understand the error...
How can I fix that it is displaying the first date with the domains correct?
The access to the values was incorrect instead of d.values.domainname or d.values.value you have to use d.values[i].domainname and d.values[i].value.
Here you can see your example running.
var data = [
{
"date": "1459468800000", // 1 April 2016
"values": [{
"name": "US",
"value": 72580613,
"domainname": "com"
}, {
"name": "US",
"value": 161645,
"domainname": "nl"
}]
}, {
"date": "1467331200000", // 1 Juli 2016
"values": [{
"name": "US",
"value": 73129243,
"domainname": "com"
}, {
"name": "US",
"value": 166152,
"domainname": "nl"
}]
}
]
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var tooltip = d3.select("body").append("div").attr("class");
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var colours = d3.scaleOrdinal()
.range(["#6F257F", "#CA0D59"]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(data.map(function(d, i) { return d.values[i].domainname; }));
y.domain([0, d3.max(data, function(d, i) { return d.values[i].value; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(25).tickFormat(function(d) { return parseInt(d / 1000) + "K"; }).tickSizeInner([-width]))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.attr("fill", "#5D6971")
.text("Aantal domeinen");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("x", function(d, i) { return x(d.values[i].domainname); })
.attr("y", function(d, i) { return y(d.values[i].value); })
.attr("width", x.bandwidth())
.attr("height", function(d, i) {return height - y(d.values[i].value)})
.attr("fill", function(d, i) { return colours(d.values[i].domainname); })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="400" height="400"></svg>
As a note the column nl looks especially small because the values are too different.
I have been working on creating a stacked bar chart using d3 v4 but with a JSON file input. This question is a continuation of a past question:Converting data structure of a JSON array for stacked bar chart.
My data array consists of numerous objects (each object is a hospital and has values such as the count of males, females, and categories).
Based from this, how can I access the individual properties of my data array and populate it accordingly? This means that when I hover over a certain bar, the gender type, count, percentage, and categories change.
**EDIT: I changed my post to show a better understanding about my data structure. Also, I do not think the provided link doesn't solve my problem that I have. I did used that method and it didn't work.
**EDIT2: Added more of my JavaScript code
The data structure is in a JSON format (An array of objects) looks like:
[{
"hospitalName": "hospital1",
"Diseases of the Circulatory System": 1,
"Diseases of the Digestive System": 1,
"Diseases of the Nervous System & Sense Organs": 2,
"Diseases of the Respiratory System": 1,
"Infectious and Parasitic Diseases": 278,
"Injury and Poisoning": 9,
"Mental Disorders": 4,
"Neoplasms": 4,
"No Diagnosis Code in Medical Record": 10,
"Perinatal Period Conditions": 1,
"Females": 223,
"Males": 88,
"Unknown": 0,
"count": 311
},
{
"hospitalName": "hospital2",
"No Diagnosis Code in Medical Record": 1,
"Females": 0,
"Males": 1,
"Unknown": 0,
"count": 1
},
{
"hospitalName": "hospital3",
"Neoplasms": 5,
"Females": 0,
"Males": 2,
"Unknown": 3,
"count": 5
}]
This is my current stacked bar chart.
The output of the tooltip would look something like this:
*My JavaScript code:
var svg = d3.select("svg"),
margin = { top: 20, right: 20, bottom: 120, 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 x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.15)
.align(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["#a95e65", "#5eaaa2", "#6b486b"]);
d3.json("hospitals2.json", function(error, data) {
if (error) throw error;
var newData = {};
data.forEach(element => {
var name = element.hospitalName;
var hospital = newData[name];
if (!hospital) {
hospital = { hospitalName: name, Females: 0, Males: 0, Unknown: 0, count: 0 };
newData[name] = hospital;
}
hospital[element.category] = +element.count;
hospital.Females += +element.Females;
hospital.Males += +element.Males;
hospital.Unknown += +element.Unknown;
hospital.count += +element.count;
});
data = [];
for (const key in newData) {
if (newData.hasOwnProperty(key)) {
data.push(newData[key]);
}
}
console.log(data);
var columns = d3.keys(data[0]);
var keys = columns.slice(1, 4);
console.log(keys);
data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d, i) { return i; }));
y.domain([0, d3.max(data, function(d) { return d.count; })]).nice();
z.domain(keys);
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d, i) { return x(i); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth())
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 60;
var yPosition = d3.mouse(this)[1] - 60;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select(".count").html("Count: " + (d[1] - d[0]) + "<br>")
tooltip.select(".percent").html("(" + (Math.round((d[1] - d[0]) / d.data.count * 100)) + "%" + ")")
})
.on("mouseover", function() {
tooltip.style("display", "inline");
})
.on("mouseout", function() {
tooltip.style("display", "none");
});
g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickFormat(function(d, i) { return data[i].MTFID }))
.selectAll("text")
.attr("x", -11)
.attr("y", 7)
.attr("dy", ".35em")
.attr("transform", "rotate(290)")
.style("text-anchor", "end");
g.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y).ticks(null, "s"))
//.call(d3.axisLeft(y).tickFormat(d => Math.round(d * 100 / d3.max(data, function(d) { return d.count })) + "%"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-family", "Montserrat, sans-serif")
.attr("font-size", "13px")
.attr("text-anchor", "start")
.text("Population");
var legend = g.append("g")
.attr("class", "legend")
.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; });
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 100)
.attr("height", 105)
.attr("fill", "#DCDCDC")
tooltip.append("text")
.attr("class", "count")
.attr("x", 50)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
tooltip.append("text")
.attr("class", "percent")
.attr("x", 50)
.attr("dy", "2em")
.style("text-anchor", "middle")
tooltip.append("text")
.attr("class", "category")
});
I am using the following horizontal bar chart (http://bl.ocks.org/juan-cb/ab9a30d0e2ace0d2dc8c) which updates based on some selections. I'm trying to add labels to the bars which would display the value inside each bar. Not sure where I'm going wrong. I initially had SVG rect elements which which I grouped under a "g" element and tried that way but still no luck. Any help will be appreciated!
JsFiddle - https://jsfiddle.net/b772s5mg/3/
JS
datasetTotal = [
{label:"Category 1", value:19},
{label:"Category 2", value:5},
{label:"Category 3", value:13},
{label:"Category 4", value:17},
{label:"Category 5", value:21},
{label:"Category 6", value:25}
];
datasetOption1 = [
{label:"Category 1", value:22},
{label:"Category 2", value:33},
{label:"Category 3", value:4},
{label:"Category 4", value:15},
{label:"Category 5", value:36},
{label:"Category 6", value:0}
];
datasetOption2 = [
{label:"Category 1", value:10},
{label:"Category 2", value:20},
{label:"Category 3", value:30},
{label:"Category 4", value:5},
{label:"Category 5", value:12},
{label:"Category 6", value:23}
];
d3.selectAll("input").on("change", selectDataset);
function selectDataset()
{
var value = this.value;
if (value == "total")
{
change(datasetTotal);
}
else if (value == "option1")
{
change(datasetOption1);
}
else if (value == "option2")
{
change(datasetOption2);
}
}
var margin = {top: (parseInt(d3.select('body').style('height'), 10)/20), right: (parseInt(d3.select('body').style('width'), 10)/20), bottom: (parseInt(d3.select('body').style('height'), 10)/20), left: (parseInt(d3.select('body').style('width'), 10)/5)},
width = parseInt(d3.select('body').style('width'), 10) - margin.left - margin.right,
height = parseInt(d3.select('body').style('height'), 10) - margin.top - margin.bottom;
var div = d3.select("body").append("div").attr("class", "toolTip");
var formatPercent = d3.format("");
var y = d3.scale.ordinal()
.rangeRoundBands([height, 0], .2, 0.5);
var x = d3.scale.linear()
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(-height)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
//.tickFormat(formatPercent);
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 + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
d3.select("input[value=\"total\"]").property("checked", true);
change(datasetTotal);
function change(dataset) {
y.domain(dataset.map(function(d) { return d.label; }));
x.domain([0, d3.max(dataset, function(d) { return d.value; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.select(".y.axis").remove();
svg.select(".x.axis").remove();
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(0)")
.attr("x", 50)
.attr("dx", ".1em")
.style("text-anchor", "end")
.text("Option %");
var bar = svg.selectAll(".bar")
.data(dataset, function(d) { return d.label; })
// new data:
.enter().append("g").append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.value); })
.attr("y", function(d) { return y(d.label); })
.attr("width", function(d) { return width-x(d.value); })
.attr("height", y.rangeBand());
bar.append("text")
.attr("x", function(d) { return x(d) - 3; })
.attr("y", 30)
.attr("dy", ".35em")
.text(function(d) { return d.value; });
var bars = d3.select("svg").selectAll("g.rects").data(dataset);
// removed data:
bars.exit().remove();
// updated data:
bars.transition()
.duration(750)
.attr("x", function(d) { return 0; })
.attr("y", function(d) { return y(d.label); })
.attr("width", function(d) { return x(d.value); })
.attr("height", y.rangeBand());
};
You may think you're adding the <text> elements to the <g> (groups) elements, but you are not!
The moment you do this...
.enter().append("g").append("rect")
... you're now trying to append the text elements to <rect> elements, and this will not work.
Solution: break your bars variable:
//appending the <g> elements
var bar = svg.selectAll(".bar")
.data(dataset, function(d) {
return d.label;
})
.enter().append("g");
//now you append the <rect> to the <g>
bar.append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.value);
})
.attr("y", function(d) {
return y(d.label);
})
.attr("width", function(d) {
return width - x(d.value);
})
.attr("height", y.rangeBand());
//and then you append the <text> to the <g>
bar.append("text")
.attr("x", function(d) {
return x(d.value) - 3;
})
.attr("text-anchor", "end")
.attr("y", function(d) {
return y(d.label) + y.rangeBand() / 2;
})
.attr("dy", ".35em")
.text(function(d) {
return d.value;
});
Here is your updated fiddle: https://jsfiddle.net/v8razxc8/
I prefer to add both rect and text inside a g element and then transform translate into position. https://jsfiddle.net/sjp700/b772s5mg/4/
var bar = svg.selectAll(".bar")
.data(dataset, function(d) { return d.label; })
// new data:
var bar_g = bar.enter().append("g")
.attr("transform", function (d) { return "translate(" + x(d.value) + "," + y(d.label) + ")"; });
bar_g.append("rect")
.attr("class", "bar")
.attr("width", function(d) { return width-x(d.value); })
.attr("height", y.rangeBand());
bar_g.append("text")
.attr("transform", function (d) { return "translate(" + 10 + "," + 10 + ")"; })
.attr("y", 30)
.attr("dy", ".35em")
.text(function(d) { return d.value;
});
I have a chart created with the code below. I want to add a second y axis positioned to the right which matches the same y scale. The reason I want the y axis on the right also is because of the limit line I have set at 16.5, the chart just doesn’t look right without the 2nd y axis.
<script>
var w = 800;
var h = 550;
var margin = {
top: 58,
bottom: 100,
left: 80,
right: 40
};
var width = w - margin.left - margin.right;
var height = h - margin.top - margin.bottom;
var threshold = 16.5;
var x = d3.scale.ordinal()
.domain(data.map(function(entry){
return entry.key;
}))
.rangeBands([0, width],.2);
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d){
return d.value;
})])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var yGridLines = d3.svg.axis()
.scale(y)
.tickSize(-width, 0, 0)
.tickFormat("")
.orient("left");
var svg = d3.select("body").append("svg")
.attr("id", "chart")
.attr("width", w)
.attr("height", h);
var chart = svg.append("g")
.classed("display", true)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
function plot(params){
this.append("g")
.call(yGridLines)
.classed("gridline", true)
.attr("transform", "translate(0,0)")
this.selectAll(".bar")
.data(params.data)
.enter()
.append("rect")
.classed("bar", true)
.attr("x", function (d,i){
return x(d.key);
})
.attr("y", function(d,i){
return y(d.value);
})
.attr("height", function(d,i){
return height - y(d.value);
})
.attr("width", function(d){
return x.rangeBand();
});
this.selectAll(".bar-label")
.data(params.data)
.enter()
.append("text")
.classed("bar-label", true)
.attr("x", function(d,i){
return x(d.key) + (x.rangeBand()/2)
})
.attr("dx", 0)
.attr("y", function(d,i){
return y(d.value);
})
.attr("dy", -6)
.text(function(d){
return d.value;
})
this.append("g")
.classed("x axis", true)
.attr("transform", "translate(" + 0 + "," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", -8)
.attr("dy" ,8)
.attr("transform", "translate(0,0) rotate(-45)");
this.append("g")
.classed("y axis", true)
.attr("transform", "translate(0,0)")
.call(yAxis);
this.select(".y.axis")
.append("text")
.attr("x", 0)
.attr("y", 0)
.style("text-anchor", "middle")
.attr("transform", "translate(-50," + height/2 + ") rotate(-90)")
.text("Downtime [Hrs]");
this.select(".x.axis")
.append("text")
.attr("x", 0)
.attr("y", 0)
.style("text-anchor", "middle")
.attr("transform", "translate(" + width/2 + ",80)")
.text("[Stations]");
// title
this.append("text")
.attr("x", (width / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("text-decoration", "underline")
.text("Over Mould");
// limit line
this.append("line")
.attr("x1", 0)
.attr("y1", y(threshold))
.attr("x2", width)
.attr("y2", y(threshold))
.attr("stroke-width", 2)
.attr("stroke", "yellow");
}
d3.json('data.json', function(data) {
plot.call(chart, {data: data});
});
</script>
Data is coming from an external JSON file.
[
{
"key": "ING_SW_CB",
"value": "7"
},
{
"key": "SB_SW_CB",
"value": "15"
},
{
"key": "NG3_SW_CB",
"value": "3"
},
{
"key": "Mould_Close",
"value": "8"
},
{
"key": "Leak_Test",
"value": "9"
},
{
"key": "ML_Load",
"value": "2"
},
{
"key": "Pre_Heat",
"value": "1"
},
{
"key": "Dispense",
"value": "5"
},
{
"key": "A310",
"value": "6"
},
{
"key": "Gelation",
"value": "5"
},
{
"key": "Platen",
"value": "7"
},
{
"key": "Mainline_Unload",
"value": "8"
},
{
"key": "De_mould",
"value": "5"
},
{
"key": "Clean_Up",
"value": "4"
},
{
"key": "Soda_Blast",
"value": "5"
},
{
"key": "RMI",
"value": "15"
},
{
"key": "Miscellaneous",
"value": "5"
}
]
My idea was to create yAxis2 and call it later in the function that plots the chart.
var yAxis2 = d3.svg.axis()
.scale(y)
.orient("right");
this.append("g")
.classed("y axis", true)
.attr("transform", "translate(width,0)")
.call(yAxis2);
When I do this it just draws it in the same place as the first yAxis, when I adjust the transform/ translate it just gets drawn at random positions on the chart, I'm trying to line it up like the left yAxis only positioned on the right. Thanks to all for your input.
Syntax error:
.attr("transform", "translate(width,0)")
To:
.attr("transform", "translate(" + width + ",0)")
plnkr