Skip to content

Instantly share code, notes, and snippets.

@laxmikanta415
Created March 21, 2018 13:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laxmikanta415/78ad8ad85bd97fe92701bfb2662d46ad to your computer and use it in GitHub Desktop.
Save laxmikanta415/78ad8ad85bd97fe92701bfb2662d46ad to your computer and use it in GitHub Desktop.
multiline chart with tooltip
license: mit
[
{
"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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment