I'm currently struggling making the tooltip work. Here's how the page looks like [See Photo1 below], and I'm trying to assign to every bar it's y value. The y value's are stored in resumeperday.json
[See below] which has 'time' and 'y' attributes for everyday. time attribute is the date, and y is how many orders were during that day. that resumeperday.json
file is stored as dataset
variable in index.html
file. I'm trying to parse its y value which is sum of all the three different orders (every date has 3 different values) and show it on the bar when mouse is hovering it. When I'm using this line of code
.html(function(d) {
return "<strong>Number of orders:</strong> <span style='color:red'>" + d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
}) + "</span>";
})
Problem: It shows only 123
for every bar (which is the maximum value of orders), I'm trying to build a function that will return the value according to the specific bar that was hovered.
Here's the index.html code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Pizza orders</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<style>
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.dot {
stroke: #000;
}
.legend {
padding: 5px;
font: 10px sans-serif;
background: yellow;
box-shadow: 2px 2px 1px #888;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}-align: center;
}
</style>
</head>
<body>
<div>
<div class="btn-group pull-right">
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
Pizza orders per hour <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a class="m" value="2018-07-15" href="#">2018-07-15</a></li>
<li><a class="m" value="2018-07-16" href="#">2018-07-16</a></li>
<li><a class="m" value="2018-07-17" href="#">2018-07-17</a></li>
<li><a class="m" value="2018-07-18" href="#">2018-07-18</a></li>
<li><a class="m" value="2018-07-19" href="#">2018-07-19</a></li>
<li><a class="m" value="2018-07-20" href="#">2018-07-20</a></li>
<li><a class="m" value="2018-07-21" href="#">2018-07-21</a></li>
</ul>
</div>
<div id="mbars">
</div>
</div>
<script type="text/javascript">
var w = 700; //width
var h = 600; //height
var padding = {top: 40, right: 40, bottom: 40, left:25};
var dataset;
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Number of orders:</strong> <span style='color:red'>" + d3.min(dataset, function(d) {
return d3.min(d, function(d) {
return d.y0 + d.y;
});
}) + "</span>";
})
//Set up stack method
var stack = d3.layout.stack();
d3.json("resumeperday.json",function(json){
dataset = json;
//Data, stacked
stack(dataset);
var color_hash = {
0 : ["50-70₪","#0000FF"],
1 : ["70.00-120₪","#FF0000"],
2 : ["120+₪","#04B404"]
};
//Set up scales
var xScale = d3.time.scale()
.domain([new Date(dataset[0][0].time),d3.time.day.offset(new Date(dataset[0][dataset[0].length-1].time),2)])
.rangeRound([0, w-padding.left-padding.right]);
var yScale = d3.scale.linear()
.domain([0,
d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
})
])
.range([h-padding.bottom-padding.top,0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(d3.time.days,1);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(10);
//Easy colors accessible via a 10-step ordinal scale
var colors = d3.scale.category10();
//Create SVG element
var svg = d3.select("#mbars")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.call(tip);
// Add a group for each row of data
var groups = svg.selectAll("g")
.data(dataset)
.enter()
.append("g")
.attr("class","rgroups")
.attr("transform","translate("+ (padding.left) + "," + (h - padding.bottom) +")")
.style("fill", function(d, i) {
return color_hash[dataset.indexOf(d)][1];
})
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
// Add a rect for each data value
var rects = groups.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("width", 2)
.style("fill-opacity",1e-6);
rects.transition()
.duration(function(d,i){
return 500 * i;
})
.ease("linear")
.attr("x", function(d) {
return xScale(new Date(d.time));
})
.attr("y", function(d) {
return -(- yScale(d.y0) - yScale(d.y) + (h - padding.top - padding.bottom)*2);
})
.attr("height", function(d) {
return -yScale(d.y) + (h - padding.top - padding.bottom);
})
.attr("width", 15)
.style("fill-opacity",1);
svg.append("g")
.attr("class","x axis")
.attr("transform","translate(40," + (h - padding.bottom) + ")")
.call(xAxis);
svg.append("g")
.attr("class","y axis")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);
// adding legend
var legend = svg.append("g")
.attr("class","legend")
.attr("x", w - padding.right - 65)
.attr("y", 25)
.attr("height", 100)
.attr("width",100);
legend.selectAll("g").data(dataset)
.enter()
.append('g')
.each(function(d,i){
var g = d3.select(this);
g.append("rect")
.attr("x", w - padding.right - 30)
.attr("y", i*25 + 10)
.attr("width", 10)
.attr("height",10)
.style("fill",color_hash[String(i)][1]);
g.append("text")
.attr("x", w - padding.right - 15)
.attr("y", i*25 + 20)
.attr("height",30)
.attr("width",100)
.style("fill",color_hash[String(i)][1])
.text(color_hash[String(i)][0]);
});
svg.append("text")
.attr("transform","translate(5,20) rotate(0)")
.attr("font-weight","bold")
.text("Pizza Orders");
svg.append("text")
.attr("class","xtext")
.attr("x",w/2 - padding.left)
.attr("y",h - 5)
.attr("text-anchor","middle")
.attr("font-weight","bold")
.text("Days");
svg.append("text")
.attr("class","title")
.attr("x", (w / 2))
.attr("y", 20)
.attr("text-anchor", "middle")
.attr("font-weight","bold")
.style("font-size", "16px")
.style("text-decoration", "underline")
.text("Number of Pizza Orders per day");
//On click, update with new data
d3.selectAll(".m")
.on("click", function() {
var date = this.getAttribute("value");
var str;
if(date == "2018-07-15"){
str = "15.json";
}else if(date == "2018-07-16"){
str = "16.json";
}else if(date == "2018-07-17"){
str = "17.json";
}else if(date == "2018-07-18"){
str = "18.json";
}else if(date == "2018-07-19"){
str = "19.json";
}
else if(date == "2018-07-20"){
str = "20.json";
}else{
str = "21.json";
}
d3.json(str,function(json){
dataset = json;
stack(dataset);
console.log(dataset);
xScale.domain([new Date(0, 0, 0,dataset[0][0].time,0, 0, 0),new Date(0, 0, 0,dataset[0][dataset[0].length-1].time,0, 0, 0)])
.rangeRound([0, w-padding.left-padding.right]);
yScale.domain([0,
d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
})
])
.range([h-padding.bottom-padding.top,0]);
xAxis.scale(xScale)
.ticks(d3.time.hour,1)
.tickFormat(d3.time.format("%H"));
yAxis.scale(yScale)
.orient("left")
.ticks(10);
groups = svg.selectAll(".rgroups")
.data(dataset);
groups.enter().append("g")
.attr("class","rgroups")
.attr("transform","translate("+ padding.left + "," + (h - padding.bottom) +")")
.style("fill",function(d,i){
return color(i);
});
rect = groups.selectAll("rect")
.data(function(d){return d;});
rect.enter()
.append("rect")
.attr("x",w)
.attr("width",1)
.style("fill-opacity",1e-6);
rect.transition()
.duration(1000)
.ease("linear")
.attr("x",function(d){
return xScale(new Date(0, 0, 0,d.time,0, 0, 0));
})
.attr("y",function(d){
return -(- yScale(d.y0) - yScale(d.y) + (h - padding.top - padding.bottom)*2);
})
.attr("height",function(d){
return -yScale(d.y) + (h - padding.top - padding.bottom);
})
.attr("width",15)
.style("fill-opacity",1);
rect.exit()
.transition()
.duration(1000)
.ease("circle")
.attr("x",w)
.remove();
groups.exit()
.transition()
.duration(1000)
.ease("circle")
.attr("x",w)
.remove();
svg.select(".x.axis")
.transition()
.duration(1000)
.ease("circle")
.call(xAxis);
svg.select(".y.axis")
.transition()
.duration(1000)
.ease("circle")
.call(yAxis);
svg.select(".xtext")
.text("Hours")
.attr("font-weight","bold");
svg.select(".title")
.text("Number of Pizza Orders per hour on " + date)
.attr("font-weight","bold");
});
});
});
</script>
</body>
</html>
resumeperday.json
file:
[
[
{
"time": "2018-07-15",
"y": 27
},
{
"time": "2018-07-16",
"y": 23
},
{
"time": "2018-07-17",
"y": 28
},
{
"time": "2018-07-18",
"y": 20
},
{
"time": "2018-07-19",
"y": 22
},
{
"time": "2018-07-20",
"y": 27
},
{
"time": "2018-07-21",
"y": 21
}
],
[
{
"time": "2018-07-15",
"y": 29
},
{
"time": "2018-07-16",
"y": 26
},
{
"time": "2018-07-17",
"y": 31
},
{
"time": "2018-07-18",
"y": 27
},
{
"time": "2018-07-19",
"y": 31
},
{
"time": "2018-07-20",
"y": 65
},
{
"time": "2018-07-21",
"y": 37
}
],
[
{
"time": "2018-07-15",
"y": 17
},
{
"time": "2018-07-16",
"y": 16
},
{
"time": "2018-07-17",
"y": 16
},
{
"time": "2018-07-18",
"y": 15
},
{
"time": "2018-07-19",
"y": 22
},
{
"time": "2018-07-20",
"y": 31
},
{
"time": "2018-07-21",
"y": 23
}
]
]
[Photo1] Only for that specific bar "123" is correct.
The problem is that you group the rects by color. If you hover over a rect you get the datum that belongs to that color group. You have no indication of the bar you are hovering over.
Select the groups by class not by element type.
var groups = svg.selectAll(".rgroups")
.data(dataset)
.enter()
.append("g")
.attr("class","rgroups")
.attr("transform","translate("+ (padding.left) + "," + (h - padding.bottom) +")")
.style("fill", function(d, i) {
return color_hash[dataset.indexOf(d)][1];
})
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
You have to transform your dataset so you have an object/array that represent all the data of a particular bar. d3.nest
might help.
Then construct a g
per bar and add the rects to this g
. Then in the tooltip you will get the datum that belongs to this bar.
Have a closer look at the style for .d3-tip.n:after
. It has an error.