Reputation: 1091
HTML
<div id="searchVolume"></div>
CSS
#tooltip {
position: absolute;
width: 50px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 12px;
line-height: 16px;
}
.indent{
padding-left: 5px;
}
rect {
-moz-transition: all 0.3s;
-webkit-transition: all 0.3s;
-o-transition: all 0.3s;
transition: all 0.3s;
}
rect:hover{
fill: orange;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
Script
var margin = {top: 25, right: 40, bottom: 35, left: 85},
w = 500 - margin.left - margin.right,
h = 350 - margin.top - margin.bottom;
var padding = 10;
var colors = [ ["Morning", "#F64BEE"],
["Midday", "#25B244"],
["Afternoon", "#2BA3F4"],
["Evening","#FD7680"]];
var dataset = [
{ "Morning": 1400000, "Midday": 673000, "Afternoon": 43000, "Evening":50000},
{ "Morning": 165000, "Midday": 160000, "Afternoon": 21000, "Evening":23000 },
{"Morning": 550000, "Midday": 301000, "Afternoon": 34000, "Evening":43000},
{"Morning": 550320, "Midday": 351000, "Afternoon": 24000, "Evening":38000},
{"Morning": 55000, "Midday": 3010, "Afternoon": 24000, "Evening":43054},
{"Morning": 750000, "Midday": 401000, "Afternoon": 84000, "Evening":42100},
{"Morning": 578000, "Midday": 306000, "Afternoon": 54000, "Evening":43400},
];
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
// ternary operator to determine if global or local has a larger scale
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) { return Math.max(d.Morning,d.Midday,d.Afternoon,d.Evening);})])
.range([h, 0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(5);
var commaFormat = d3.format(',');
//SVG element
var svg = d3.select("#searchVolume")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Graph Bars
var sets = svg.selectAll(".set")
.data(dataset)
.enter()
.append("g")
.attr("class","set")
.attr("transform",function(d,i){
return "translate(" + xScale(i) + ",0)";
})
;
sets.append("rect")
.attr("class","Morning")
.attr("width", xScale.rangeBand()/4)
.attr("y", function(d) {
return yScale(d.Morning);
})
.attr("x", xScale.rangeBand()/4)
.attr("height", function(d){
return h - yScale(d.Morning);
})
.attr("fill", colors[0][1])
.append("text")
.text(function(d) {
return commaFormat(d.Morning);
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 4;
})
.attr("y", function(d) {
return h - yScale(d.Morning) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black")
;
sets.append("rect")
.attr("class","Midday")
.attr("width", xScale.rangeBand()/4)
.attr("y", function(d) {
return yScale(d.Midday);
})
.attr("height", function(d){
return h - yScale(d.Midday);
})
.attr("fill", colors[1][1])
.append("text")
.text(function(d) {
return commaFormat(d.Midday);
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 4;
})
.attr("y", function(d) {
return h - yScale(d.Midday) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "red")
;
sets.append("rect")
.attr("class","Afternoon")
.attr("width", xScale.rangeBand()/4)
.attr("y", function(d) {
return yScale(d.Afternoon);
})
.attr("height", function(d){
return h - yScale(d.Afternoon);
})
.attr("fill", colors[2][1])
.append("text")
.text(function(d) {
return commaFormat(d.Afternoon);
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 4;
})
.attr("y", function(d) {
return h - yScale(d.Afternoon) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "red")
;
sets.append("rect")
.attr("class","Evening")
.attr("width", xScale.rangeBand()/4)
.attr("y", function(d) {
return yScale(d.Evening);
})
.attr("height", function(d){
return h - yScale(d.Evening);
})
.attr("fill", colors[3][1])
.append("text")
.text(function(d) {
return commaFormat(d.Evening);
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 4;
})
.attr("y", function(d) {
return h - yScale(d.Evening) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "red")
;
// xAxis
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + (h) + ")")
.call(xAxis)
;
// yAxis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0 ,0)")
.call(yAxis)
;
// xAxis label
svg.append("text")
.attr("transform", "translate(" + (w / 4) + " ," + (h + margin.bottom - 5) +")")
.style("text-anchor", "middle")
.text("Keyword");
//yAxis label
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x", 0 - (h / 4))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Searches");
// Title
svg.append("text")
.attr("x", (w / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("text-decoration", "underline")
.text("Weekly Consumption");
// add legend
var legend = svg.append("g")
.attr("class", "legend")
//.attr("x", w - 65)
//.attr("y", 50)
.attr("height", 100)
.attr("width", 100)
.attr('transform', 'translate(-20,50)');
var legendRect = legend.selectAll('rect').data(colors);
legendRect.enter()
.append("rect")
.attr("x", w - 65)
.attr("width", 10)
.attr("height", 10);
legendRect
.attr("y", function(d, i) {
return i * 20;
})
.style("fill", function(d) {
return d[1];
});
var legendText = legend.selectAll('text').data(colors);
legendText.enter()
.append("text")
.attr("x", w - 52);
legendText
.attr("y", function(d, i) {
return i * 20 + 9;
})
.text(function(d) {
return d[0];
});
In the above fiddle, I have attempted to make a bar chart using d3.js library. I am stuck on following basic things which shouldn't even take much time. I am not able to grasp the functions of d3. You can play around with the fiddle and any help would be hugely beneficial:
1. I want four different bars grouped on a single x value. Like for '0' there would be four of them unlike the current one where it is merging everything into two.
2. Change the content of x-axis from numbers to days like from Monday to Friday.
3. For y-axis, I am trying to display the values like, instead of 20000, it should show 20k and the bar should recognise that while dynamically creating it. Is this possible?
Any help would be greatly beneficial. I couldn't figure it out.
Upvotes: 0
Views: 300
Reputation: 2229
The main issue is that you start joining your data correctly but afterwards you start doing some manual stuff that could have been solved by using the data
function to join the data into your child elements. Let me explain:
First of all we will need two x axis scales, one to hold our days domain and the other one will hold our time domain using as rangeRoundBands
the days scale.
var day_scale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var time_scale = d3.scale.ordinal();
time_scale.domain(['Morning', 'Midday', 'Afternoon', 'Evening'])
.rangeRoundBands([0, day_scale.rangeBand()]);
Let's tackle the x axis formatting while we are setting up our scales. I created an array of days and within our tickFormat
function lets return the value in the array based in the index of the data we passed.
var days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
var day_axis = d3.svg.axis()
.scale(day_scale)
.orient("bottom")
.tickFormat(function(d,i) {
return days[i];
});
Now the y axis formatting, we can solve this by using a d3.formatPrefix
more info here
var prefix = d3.formatPrefix(1.21e9);
var searches_axis = d3.svg.axis()
.scale(searches_scale)
.orient("left")
.ticks(5)
.tickFormat(function(d) {
var prefix = d3.formatPrefix(d);
return prefix.scale(d) + prefix.symbol;
});
Now lets skip our svg and axis config, to get to the data join issue:
var day_groups = svg.selectAll(".day-group")
.data(dataset) // join data to our selection
.enter().append("g")
.attr("class", function(d, i) {
return 'day-group day-group-' + i;
})
.attr("transform", function(d, i) {
// position a g element with our day_scale and data index
return "translate(" + day_scale(i) + ",0)";
});
With our day-groups positioned correctly now we are able to append our time data.
var times_g = day_groups.selectAll(".time-group")
.data(function(d) {
// this is the tricky part, we are creating an array of
// objects with key (time...'Morning', 'Midday', 'Afternoon', 'Evening')
// and value (the value of the time)
// in order to create a time group for each time event
return Object.keys(d).map(function(key) {
return {
key: key,
value: d[key]
}
});
})
.enter().append("g")
.attr("class", function(d) {
return 'time-group time-group-' + d.key;
})
.attr("transform", function(d) {
// use our time scale to position
return "translate(" + time_scale(d.key) + ",0)";
});
Now lets add our rects!
var rects = times_g.selectAll('.rect')
.data(function(d) {
// use as data our object
return [d];
})
.enter().append("rect")
.attr("class", "rect")
.attr("width", time_scale.rangeBand()) // get width of rect based in our time_scale
.attr("x", function(d) {
return 0; // returning 0 since the group is in charge of positioning
})
.attr("y", function(d) {
return searches_scale(d.value); // use our y_scale
})
.attr("height", function(d) {
return h - searches_scale(d.value); // use our y_scale
})
.style("fill", function(d) {
return colors[d.key]; // map colors by using an object
});
Mapping color object:
var colors = {
"Morning":"#F64BEE",
"Midday": "#25B244",
"Afternoon": "#2BA3F4",
"Evening": "#FD7680"
};
If you have any doubt of how this works here is the updated jsfiddle: https://jsfiddle.net/706gsjfg/3/ (I removed certain things, but you can add them later I guess)
Upvotes: 2