Last active
November 12, 2018 16:09
-
-
Save JulienAssouline/574a52ee2034bcdc1e56ed926f36dd52 to your computer and use it in GitHub Desktop.
Line chart with tooltip
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Date | randNumCol | something | ||
---|---|---|---|---|
0 | August 2018 | 7 | something1 | |
1 | August 2018 | 31 | something2 | |
2 | August 2018 | 18 | something3 | |
3 | August 2018 | 6 | something4 | |
4 | August 2018 | 1 | something5 | |
5 | August 2018 | 12 | something6 | |
6 | August 2018 | 23 | something7 | |
7 | August 2018 | 2 | something8 | |
8 | August 2018 | 8 | something9 | |
9 | August 2018 | 2 | something10 | |
10 | August 2018 | 13 | something11 | |
11 | September 2018 | 33 | something1 | |
12 | September 2018 | 21 | something2 | |
13 | September 2018 | 35 | something3 | |
14 | September 2018 | 10 | something4 | |
15 | September 2018 | 23 | something5 | |
16 | September 2018 | 20 | something6 | |
17 | September 2018 | 5 | something7 | |
18 | September 2018 | 29 | something8 | |
19 | September 2018 | 3 | something9 | |
20 | September 2018 | 6 | something10 | |
21 | September 2018 | 35 | something11 | |
22 | October 2018 | 2 | something1 | |
23 | October 2018 | 8 | something2 | |
24 | October 2018 | 9 | something3 | |
25 | October 2018 | 11 | something4 | |
26 | October 2018 | 37 | something5 | |
27 | October 2018 | 30 | something6 | |
28 | October 2018 | 30 | something7 | |
29 | October 2018 | 13 | something8 | |
30 | October 2018 | 35 | something9 | |
31 | October 2018 | 27 | something10 | |
32 | October 2018 | 36 | something11 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<link href="https://fonts.googleapis.com/css?family=Playfair+Display" rel="stylesheet"> | |
<style type="text/css"> | |
/* No style rules here yet */ | |
body,html{ | |
margin: 0; | |
padding: 0; | |
font-family: 'Playfair Display', serif; | |
font-size: 11px; | |
text-align: center; | |
} | |
.states { | |
fill: #ccc; | |
stroke: #fff; | |
} | |
#button{ | |
position: absolute; | |
cursor: pointer; | |
background-color: #e7e7e7; /* Green */ | |
border: none; | |
color: black; | |
padding: 10px 10px; | |
/*text-align: center; | |
text-decoration: none; | |
display: inline-block; | |
font-size: 13px;*/ | |
} | |
button:focus {outline:0;} | |
#checkbox-buttons{ | |
display: none; | |
position: absolute; | |
overflow:auto; | |
width: 100%; | |
} | |
#input_wrapper{ | |
position: absolute; | |
display: none; | |
} | |
input{ | |
background-color: #fff; | |
border: 13px solid #333; | |
background: #fff; | |
color: #333; | |
} | |
#legend{ | |
position: absolute; | |
left: 50px; | |
top: 450px; | |
width: 100%; | |
display: inline-block; | |
padding: 0.5em 0 0 0em; | |
margin-right: 1em; | |
} | |
.legend{ | |
width: auto; | |
padding-left:10px | |
} | |
.color { | |
width: 10px; | |
display: inline-block; | |
height: 10px; | |
background-color: red; | |
} | |
.label { | |
display: inline-block; | |
padding: 1em 0 0 0.4em | |
} | |
.axisx path { | |
fill: none; | |
stroke: white; | |
stroke-width: 2px; | |
shape-rendering: crispEdges; | |
} | |
.axisy path { | |
fill: none; | |
stroke: white; | |
} | |
.axisy line{ | |
fill: none; | |
stroke: #dcd9d3; | |
stroke-width: 1px; | |
shape-rendering: crispEdges; | |
opacity: 0.7; | |
stroke-dasharray: 3,3; | |
} | |
.title{ | |
font-size: 20px; | |
text-align:left; | |
padding: 0em 0 0 3em; | |
} | |
.subtitle{ | |
font-size: 15px; | |
text-align:left; | |
padding: 0em 0 0 4em; | |
} | |
/* h2{ | |
position: absolute; | |
left: 355px; | |
font-size: 20px; | |
}*/ | |
#arrow { | |
transform: rotate(90deg); | |
display: inline-block; | |
} | |
#lines{ | |
fill: none; | |
} | |
/*.checkmark { | |
position: absolute; | |
top: 0; | |
left: 0; | |
height: 10px; | |
width: 10px; | |
background-color: #eee; | |
}*/ | |
</style> | |
</head> | |
<body> | |
<div id="wrapper"> | |
</div> | |
<button id="button">Highlight/Unhighlight Company <span id="arrow"> ❯</span></button> | |
<div id = "checkbox-buttons"> | |
</div> | |
<script> | |
var clicked = false; | |
var wrapper = d3.select("#wrapper") | |
// wrapper.append("div").attr("id", "checkbox-buttons") | |
// var checkbox_buttons = d3.select("#checkbox-buttons") | |
var button = document.getElementById("button") | |
wrapper.append("svg").attr("id", "chart") | |
var h = 400; | |
function redraw() { | |
var margin = { | |
left: 80, | |
top: 40, | |
bottom: 40, | |
right: 100 | |
} | |
var w = wrapper.node().getBoundingClientRect().width | |
var width = w - margin.left - margin.right, | |
height = h - margin.top - margin.bottom; | |
// wrapper.append("div").attr("id", "legend") | |
var svg = d3.select("#chart").attr("width", w) | |
.attr("height", h) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
wrapper.append("div") | |
.attr("id", "tooltip") | |
.style("opacity", 1) | |
.style("position", "absolute") | |
var parseTime = d3.timeParse("%B %Y") | |
var xScale = d3.scaleTime() | |
.range([0, width]); | |
var yScale = d3.scaleLinear() | |
.range([height, 0]) | |
var line = d3.line() | |
.x(function(d) { return xScale(d.Date); }) | |
.y(function(d) { return yScale(d.randNumCol); }); | |
var xtickFormat = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October"] | |
var xAxis = d3.axisBottom() | |
.scale(xScale) | |
// .tickFormat(function(d,i){ return xtickFormat[i] }) | |
.ticks(2) | |
.tickSizeOuter(0) | |
var yAxis = d3.axisLeft() | |
.scale(yScale) | |
// .tickValues(xtickValues) | |
.tickSize(-width,0,0) | |
.tickSizeOuter(0) | |
var color = d3.scaleOrdinal() | |
.domain(11) | |
.range(["lightgrey"]); | |
d3.csv("df_example.csv", function(error, data){ | |
data.forEach(function(d){ | |
d.randNumCol = +d.randNumCol | |
d.Date = parseTime(d.Date) | |
}) | |
xScale.domain(d3.extent(data, function(d) { return d.Date; })); | |
yScale.domain(d3.extent(data, function(d) { return d.randNumCol })) | |
// checkbox | |
d3.selectAll(".checkboxs").remove() | |
d3.selectAll(".checkbox_label").remove() | |
d3.selectAll("#input_wrapper").remove() | |
// get unqiue keys | |
var key_array = [...new Set(data.map(d => d.something))]; | |
var svg_2 = d3.select("#check").attr("width", w) | |
.attr("height", h) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
button.style.left = 60 + "px" | |
button.style.top = h + 90+ "px" | |
var input_width = wrapper.node().getBoundingClientRect().width/2.5 | |
wrapper.append("div").attr("id", "input_wrapper").attr("class", "hidden") | |
.style("left", 60+"px") | |
.style("top", (h +140)+"px") | |
.selectAll(".inputs") | |
.data(key_array) | |
.enter() | |
.append("label") | |
.append("input") | |
.attr("type", "checkbox") | |
.attr("class", "checkboxs") | |
.property('checked', true) | |
.attr("value", function(d){ | |
return d | |
}) | |
d3.selectAll("label") | |
.data(key_array) | |
.attr("class", "checkbox_label") | |
.append("text").text(function (d) { | |
return " " +d | |
}) | |
var checkbox_buttons = d3.select("#input_wrapper") | |
var checkbox_buttons_node = checkbox_buttons._groups[0][0] | |
button.addEventListener("click", function(d){ | |
if (checkbox_buttons_node.className == "hidden") { | |
checkbox_buttons_node.className = "show" | |
checkbox_buttons_node.style.display = "inline-block" | |
} | |
else if (checkbox_buttons_node.className == "show") { | |
checkbox_buttons_node.className = "hidden" | |
checkbox_buttons_node.style.display = "none" | |
} | |
}) | |
// data processing | |
var inputs = d3.selectAll(".checkboxs") | |
var checkox_array = []; | |
inputs._groups[0].forEach(function(d){ | |
checkox_array.push({ | |
checked: d.checked, | |
something: d.value | |
}) | |
}) | |
var data_checked = []; | |
data.forEach(function(d, i){ | |
data_checked.push(Object.assign({}, d, checkox_array[i])) | |
}) | |
// checkox_array | |
var dataNest = d3.nest() | |
.key(function(d) {return d.something;}) | |
.entries(data_checked); | |
// axis | |
d3.selectAll(".axisx").remove() | |
svg.append("g") | |
.attr("class", "axisx") | |
svg.select(".axisx") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis) | |
d3.selectAll(".axisy").remove() | |
svg.append("g") | |
.attr("class", "axisy") | |
svg.select(".axisy").attr("transform", "translate(0,0)") | |
.call(yAxis) | |
.selectAll("text") | |
.attr("transform", "translate("+ 0 + ",0)") | |
var checked_boxes =d3.selectAll("input[type=checkbox]") | |
// tooltip | |
var tooltip = d3.select("#tooltip") | |
var tooltipLine = svg.append("line"); | |
// text | |
var text = svg.selectAll(".text") | |
.data(dataNest) | |
d3.selectAll(".text").remove() | |
var text_enter = text.enter() | |
.append("text") | |
.attr("class", function(d) { | |
return "text " + d.values[0].something.replace(/ /g, "") | |
}) | |
console.log(dataNest) | |
var text_update = text.merge(text_enter) | |
text_update.attr("x", function(d) { | |
return width + 5 | |
}) | |
.attr("y", function(d) { | |
return yScale(d.values[0].randNumCol) | |
}) | |
text_update.text(function(d) { | |
return d.something | |
}) | |
.style("font-size", "12px") | |
.style("display", function(d) { | |
if(d.values[0].checked == true) return "block" | |
else return "none" | |
}) | |
var circles = svg.selectAll(".circles") | |
.data(data) | |
d3.selectAll(".circle").remove() | |
var circles_enter = circles.enter() | |
.append("circle") | |
.attr("class", "circle") | |
.attr("r", 3) | |
var circles_update = circles.merge(circles_enter) | |
circles_update.attr("cx", function(d){ | |
return xScale(d.Date) | |
}) | |
.attr("cy", function(d){ | |
return yScale(d.randNumCol) | |
}) | |
.attr("fill", function(d){ | |
return color(d.something) | |
}) | |
// .on("mouseover", function(d){ | |
// const line_hover = Math.floor(xScale.invert(d3.mouse(this)[0])); | |
// console.log(d3.mouse(this)) | |
// tooltipLine.attr("stroke", "grey") | |
// .attr("x1", xScale(line_hover)) | |
// .attr("x2", xScale(line_hover)) | |
// .attr("y1", 0) | |
// .attr("y2", height) | |
// .attr("class", "line_hover") | |
// .style('stroke-width', 1) | |
// }) | |
// .on("mouseout", function(){ | |
// if (tooltip) tooltip.style('display', 'none') | |
// }) | |
// lines | |
var lines = svg.selectAll(".lines") | |
.data(dataNest) | |
// d3.selectAll(".nested").remove() | |
var lines_enter = lines.enter() | |
.append("g") | |
.attr("class", "nested") | |
var lines_update = lines.merge(lines_enter) | |
lines_update.append("path") | |
.attr("class", function(d) { | |
return "line " + d.values[0].something.replace(/ /g, "") | |
}) | |
.attr("d", function(d) { return line(d.values)} ) | |
.style("fill", 0) | |
.attr("id", "lines") | |
lines_update.style("stroke", function(d, i) { | |
if(d.values[0].checked == true) return color(i) | |
else return "#e4e4e4" | |
}) | |
.style("stroke-width", 2.5) | |
.on("click", function(d) { | |
if (clicked == false) { | |
var path_node = this.childNodes[0] | |
d3.selectAll("path").style("opacity", 0.3) | |
d3.select(path_node).style("opacity", 1).style("stroke-width", 2.5) | |
d3.selectAll(".text").style("opacity", 0.3) | |
d3.select("." + d.values[0].something.replace(/ /g, "")).style("opacity", 1) | |
clicked = true | |
} | |
else { | |
d3.selectAll("path").style("opacity", 1).style("stroke-width", 2.5) | |
d3.selectAll(".text").style("opacity", 1) | |
clicked = false | |
} | |
}) | |
// .on("mouseout", function() { | |
// d3.selectAll("path").style("opacity", 1).style("stroke-width", 2.5) | |
// d3.selectAll(".text").style("opacity", 1) | |
// }) | |
var score_array = []; | |
data.forEach(function(d) { | |
score_array.push(d.randNumCol) | |
}) | |
var max_score = Math.max.apply(null, score_array) | |
d3.selectAll(".line_hover").remove() | |
var tipBox = svg.append("rect") | |
.attr("width", width) | |
.attr("height", height) | |
.attr("opacity", 0) | |
.on("mousemove", drawTooltip) | |
.on("mouseout", removeTooltip) | |
function removeTooltip() { | |
if (tooltip) tooltip.style('display', 'none'); | |
if (tooltipLine) tooltipLine.attr('stroke', 'none'); | |
} | |
var bisectDate = d3.bisector(function(d) { return d.Date; }).left | |
function drawTooltip(){ | |
const line_hover = xScale.invert(d3.mouse(this)[0]); | |
var date_hover = xScale.invert(d3.mouse(this)[0]).getMonth() | |
var dateobj = xScale.invert(d3.mouse(this)[0]); | |
if(dateobj.getDate() > 15) date_hover = date_hover + 1; | |
// yScale.invert(pos.y) | |
tooltipLine.attr("stroke", "grey") | |
.attr("x1", xScale(line_hover)) | |
.attr("x2", xScale(line_hover)) | |
.attr("y1", 0) | |
.attr("y2", height) | |
.attr("class", "line_hover") | |
.style('stroke-width', 1) | |
tooltip.html(date_hover) | |
.style("position", "absolute") | |
.style("background-color", "lightgrey") | |
.style('display', 'block') | |
.style('left', d3.event.pageX - 100+ "px") | |
.style('top', d3.event.pageY - 20+"px") | |
.selectAll() | |
.data(dataNest).enter() | |
.append('div') | |
.style('color', "black") | |
.html(function(e){ return e.key + ': ' + e.values.find(function(h){ return (h.Date.getMonth() + 0.5) == (date_hover + 0.5) }).randNumCol}) | |
} | |
d3.selectAll(".checkboxs") | |
.on("change", function(el){ | |
var node = this | |
lines_update.style("stroke", function(d, i) { | |
if (d.values[0].something == node.value && d.values[0].checked != node.checked) { | |
d.values[0].checked = node.checked | |
} | |
if(d.values[0].checked == true) return color(i) | |
else return "#e4e4e4" | |
}) | |
.style("stroke-width", 2.5) | |
text_update.text(function(d) { | |
return d.values[0].something | |
}) | |
.style("font-size", "12px") | |
.style("display", function(d) { | |
if (d.values[0].something == node.value && d.values[0].checked != node.checked) { | |
d.values[0].checked = node.checked | |
} | |
if(d.values[0].checked == true) return "block" | |
else return "none" | |
}) | |
}) | |
// var legend_html = d3.select("#legend") | |
// var legend = legend_html.selectAll(".legend-item") | |
// .data(key_array) | |
// d3.selectAll(".legend").remove() | |
// var legend_enter = legend.enter() | |
// .append("span") | |
// .attr("class", "legend") | |
// legend_enter.append("div").attr("class", "color") | |
// legend_enter.append("span").attr("class", "label") | |
// var legend_update = legend.merge(legend_enter) | |
// legend_update.select(".color") | |
// .style("background-color", function(d, i) { | |
// return color(i) | |
// }) | |
// .style("border", "1px") | |
// legend_update.select(".label") | |
// .html(function(d) { | |
// return d | |
// }) | |
// checkbox logic | |
}) | |
} // redraw | |
redraw() | |
d3.select(window).on("resize", redraw) | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment