Built with blockbuilder.org
forked from anonymous's block: multiline chart with tooltip
license: mit |
Built with blockbuilder.org
forked from anonymous's block: multiline chart with tooltip
[ | |
{ | |
"date": "3/7/2018", | |
"name": "IV30 20-Day MA", | |
"value": 24.5533 | |
}, | |
{ | |
"date": "3/6/2018", | |
"name": "IV30 20-Day MA", | |
"value": 24.7956 | |
}, | |
{ | |
"date": "3/5/2018", | |
"name": "IV30 20-Day MA", | |
"value": 25.2112 | |
}, | |
{ | |
"date": "3/2/2018", | |
"name": "IV30 20-Day MA", | |
"value": 25.3001 | |
}, | |
{ | |
"date": "3/1/2018", | |
"name": "IV30 20-Day MA", | |
"value": 25.1644 | |
}, | |
{ | |
"date": "2/28/2018", | |
"name": "IV30 20-Day MA", | |
"value": 24.9969 | |
}, | |
{ | |
"date": "2/27/2018", | |
"name": "IV30 20-Day MA", | |
"value": 24.9828 | |
}, | |
{ | |
"date": "2/26/2018", | |
"name": "IV30 20-Day MA", | |
"value": 25.0242 | |
}, | |
{ | |
"date": "2/23/2018", | |
"name": "IV30 20-Day MA", | |
"value": 25.0468 | |
}, | |
{ | |
"date": "2/22/2018", | |
"name": "IV30 20-Day MA", | |
"value": 24.9643 | |
}, | |
{ | |
"date": "2/21/2018", | |
"name": "IV30 20-Day MA", | |
"value": 24.714 | |
}, | |
{ | |
"date": "2/20/2018", | |
"name": "IV30 20-Day MA", | |
"value": 24.4649 | |
}, | |
{ | |
"date": "2/16/2018", | |
"name": "IV30 20-Day MA", | |
"value": 24.2767 | |
}, | |
{ | |
"date": "2/15/2018", | |
"name": "IV30 20-Day MA", | |
"value": 24.0804 | |
}, | |
{ | |
"date": "2/14/2018", | |
"name": "IV30 20-Day MA", | |
"value": 23.9034 | |
}, | |
{ | |
"date": "2/13/2018", | |
"name": "IV30 20-Day MA", | |
"value": 23.7368 | |
}, | |
{ | |
"date": "2/12/2018", | |
"name": "IV30 20-Day MA", | |
"value": 23.4934 | |
}, | |
{ | |
"date": "3/7/2018", | |
"name": "IV 30", | |
"value": 23.946 | |
}, | |
{ | |
"date": "3/6/2018", | |
"name": "IV 30", | |
"value": 22.547 | |
}, | |
{ | |
"date": "3/5/2018", | |
"name": "IV 30", | |
"value": 23.256 | |
}, | |
{ | |
"date": "3/2/2018", | |
"name": "IV 30", | |
"value": 24.454 | |
}, | |
{ | |
"date": "3/1/2018", | |
"name": "IV 30", | |
"value": 25.649 | |
}, | |
{ | |
"date": "2/28/2018", | |
"name": "IV 30", | |
"value": 22.837 | |
}, | |
{ | |
"date": "2/27/2018", | |
"name": "IV 30", | |
"value": 20.738 | |
}, | |
{ | |
"date": "2/26/2018", | |
"name": "IV 30", | |
"value": 20.011 | |
}, | |
{ | |
"date": "2/23/2018", | |
"name": "IV 30", | |
"value": 21.482 | |
}, | |
{ | |
"date": "2/22/2018", | |
"name": "IV 30", | |
"value": 23.635 | |
}, | |
{ | |
"date": "2/21/2018", | |
"name": "IV 30", | |
"value": 23.214 | |
}, | |
{ | |
"date": "2/20/2018", | |
"name": "IV 30", | |
"value": 22.367 | |
}, | |
{ | |
"date": "2/16/2018", | |
"name": "IV 30", | |
"value": 22.186 | |
}, | |
{ | |
"date": "2/15/2018", | |
"name": "IV 30", | |
"value": 23.122 | |
}, | |
{ | |
"date": "2/14/2018", | |
"name": "IV 30", | |
"value": 23.409 | |
}, | |
{ | |
"date": "2/13/2018", | |
"name": "IV 30", | |
"value": 26.298 | |
}, | |
{ | |
"date": "2/12/2018", | |
"name": "IV 30", | |
"value": 27.488 | |
} | |
] |
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<style> | |
body { font: 12px Arial;} | |
.bar{ | |
fill: rgb(44, 160, 44); | |
} | |
path { | |
stroke: steelblue; | |
stroke-width: 2; | |
fill: none; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: grey; | |
stroke-width: 1; | |
shape-rendering: crispEdges; | |
} | |
.legend { | |
font-size: 16px; | |
font-weight: bold; | |
text-anchor: middle; | |
text-decoration: underline; | |
} | |
</style> | |
</head> | |
<body> | |
<div id='tooltip' style='position:absolute;background-color:lightgray;padding:5px'></div> | |
<script> | |
var data = []; | |
var margin = {top: 60, right: 50, bottom: 200, left: 50}, | |
width = 700 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom; | |
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 color = d3.scaleOrdinal(d3.schemeCategory10); | |
var parseDate = d3.timeParse("%m/%d/%Y"); | |
var formatDate = d3.timeFormat("%m/%d/%Y"); | |
var x = d3.scaleBand().range([0, width]).padding(0.3); | |
var y = d3.scaleLinear().range([height, 0]); | |
// Define the axes | |
var xAxis = d3.axisBottom().scale(x). | |
ticks(5).tickFormat(formatDate); | |
var yAxis = d3.axisLeft().scale(y).ticks(5); | |
var valueline = d3.line() | |
.x(function(d) { return x(d.date); }) | |
.y(function(d) { return y(d.value); }); | |
const tooltip = d3.select('#tooltip'); | |
const tooltipLine = svg.append('line'); | |
let tipBox; | |
tipBox = svg.append('rect') | |
.attr('width', width) | |
.attr('height', height) | |
.attr('opacity', 0) | |
.on('mousemove', drawTooltip) | |
.on('mouseout', removeTooltip); | |
d3.json('data.json',(err,d) => { | |
d.forEach(di => { | |
data.push({ | |
date: parseDate(di.date), | |
name: di.name, | |
value: +di.value | |
}); | |
}); | |
x.domain(data.map(function(d) { return d.date; })); | |
y.domain([0, d3.max(data, d => d.value)]); | |
var dataNest = d3.nest() | |
.key(function(d) {return d.name;}) | |
.entries(data); | |
x.invert = (function(){ | |
var domain = x.domain() | |
var range = x.range() | |
var scale = d3.scaleQuantize().domain(range).range(domain) | |
return function(x){ | |
return scale(x) | |
} | |
})() | |
dataNest.forEach(function(d,i) { | |
svg.append("path") | |
.attr("class", "line") | |
.style("stroke", () => d.color = color(d.key)) | |
.attr("id", 'tag'+d.key.replace(/\s+/g, '')) | |
.attr("d", valueline(d.values)); | |
legendSpace =125; | |
svg.append("text") | |
.attr("x", (legendSpace/2)+i*legendSpace) // space legend | |
.attr("y", -20 ) | |
.attr("class", "legend") // style the legend | |
.style("fill", function() { // Add the colours dynamically | |
return d.color = color(d.key); }) | |
.on("click", function(){ | |
// Determine if current line is visible | |
var active = d.active ? false : true, | |
newOpacity = active ? 0 : 1; | |
// Hide or show the elements based on the ID | |
d3.select("#tag"+d.key.replace(/\s+/g, '')) | |
.transition().duration(100) | |
.style("opacity", newOpacity); | |
// Update whether or not the elements are active | |
d.active = active; | |
this.innerHTML = d.active ? d.key+'+' : d.key+'-'; | |
}) | |
.text(d.key+'-'); | |
}); | |
svg.append("text") | |
.attr("x", (legendSpace/2)+3*legendSpace) // space legend | |
.attr("y", -20 ) | |
.attr("class", "legend") // style the legend | |
.style("fill", function() { // Add the colours dynamically | |
return d.color = color(d.key); }) | |
.on("click", function(){ | |
var op = document.getElementById('bars-group').style.opacity; | |
var newOp = (op ==1 ? 0 : 1); | |
document.getElementById('bars-group').style.opacity = newOp; | |
if(newOp){ | |
this.innerHTML = 'Option Volume -'; | |
}else{ | |
this.innerHTML ='Option Volume +'; | |
} | |
}) | |
.text('Option Volume -'); | |
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(-70)" );; | |
svg.append("g") | |
.attr("class", "y axis") | |
.call(yAxis); | |
var optionData = [ | |
{ | |
"date": "3/7/2018", | |
"name": "Options Volume", | |
"value": 195 | |
}, | |
{ | |
"date": "3/6/2018", | |
"name": "Options Volume", | |
"value": 21629 | |
}, | |
{ | |
"date": "3/5/2018", | |
"name": "Options Volume", | |
"value": 22606 | |
}, | |
{ | |
"date": "3/2/2018", | |
"name": "Options Volume", | |
"value": 41022 | |
}, | |
{ | |
"date": "3/1/2018", | |
"name": "Options Volume", | |
"value": 45224 | |
}, | |
{ | |
"date": "2/28/2018", | |
"name": "Options Volume", | |
"value": 28676 | |
}, | |
{ | |
"date": "2/27/2018", | |
"name": "Options Volume", | |
"value": 39206 | |
}, | |
{ | |
"date": "2/26/2018", | |
"name": "Options Volume", | |
"value": 37144 | |
}, | |
{ | |
"date": "2/23/2018", | |
"name": "Options Volume", | |
"value": 32383 | |
}, | |
{ | |
"date": "2/22/2018", | |
"name": "Options Volume", | |
"value": 30552 | |
}, | |
{ | |
"date": "2/21/2018", | |
"name": "Options Volume", | |
"value": 35690 | |
}, | |
{ | |
"date": "2/20/2018", | |
"name": "Options Volume", | |
"value": 29094 | |
}, | |
{ | |
"date": "2/16/2018", | |
"name": "Options Volume", | |
"value": 50932 | |
}, | |
{ | |
"date": "2/15/2018", | |
"name": "Options Volume", | |
"value": 61914 | |
}, | |
{ | |
"date": "2/14/2018", | |
"name": "Options Volume", | |
"value": 55138 | |
}, | |
{ | |
"date": "2/13/2018", | |
"name": "Options Volume", | |
"value": 35172 | |
}, | |
{ | |
"date": "2/12/2018", | |
"name": "Options Volume", | |
"value": 40909 | |
} | |
]; | |
var optData = []; | |
optionData.forEach(di => { | |
optData.push({ | |
date: parseDate(di.date), | |
name: di.name, | |
value: +di.value | |
}); | |
}); | |
var yscaleRight = d3.scaleLinear(). | |
domain([0, d3.max(optData, function(d) { | |
return d.value; })]).range([height, 0]); | |
var yAxisRight = d3.axisRight().scale(yscaleRight).ticks(5); | |
svg.append('g').attr('id','bars-group') | |
.style('opacity',1).selectAll(".bar") | |
.data(optData) | |
.enter().append("rect") | |
.attr("class", "bar") | |
.attr("x", function(d) { return x(d.date); }) | |
.attr("y", function(d) { return yscaleRight(d.value); }) | |
.attr("width", x.bandwidth()) | |
.attr("height", function(d) { return height - yscaleRight(d.value); }); | |
svg.append("g") | |
.attr("class", "y axis") | |
.attr("transform", "translate( " + width + ", 0 )") | |
.call(yAxisRight); | |
}); | |
function removeTooltip() { | |
if (tooltip) tooltip.style('display', 'none'); | |
if (tooltipLine) tooltipLine.attr('stroke', 'none'); | |
} | |
function drawTooltip() { | |
const date = x.invert(d3.mouse(tipBox.node())[0]) | |
tooltipLine.attr('stroke', 'black') | |
.attr('x1', x(date)) | |
.attr('x2', x(date)) | |
.attr('y1', 0) | |
.attr('y2', height); | |
var dataSelected = []; | |
var t = data.forEach(id => { | |
if((new Date(id.date).getTime() == new Date(date).getTime()) ){ | |
dataSelected.push(id); | |
} | |
}); | |
tooltip.html(formatDate(date)) | |
.style('display', 'block') | |
.style('left', d3.event.pageX + 20+'px') | |
.style('top', d3.event.pageY - 20+'px') | |
.selectAll() | |
.data(dataSelected).enter() | |
.append('div') | |
.style('color', (d,i) => { | |
if(d.name=='IV 30'){ | |
return "blue"; | |
}else{ | |
return "red"; | |
} | |
}) | |
.html(d => d.name + "<br/>" + d.value) ; | |
} | |
</script> |