Skip to content

Instantly share code, notes, and snippets.

@JulienAssouline
Last active November 12, 2018 16:09
Show Gist options
  • Save JulienAssouline/574a52ee2034bcdc1e56ed926f36dd52 to your computer and use it in GitHub Desktop.
Save JulienAssouline/574a52ee2034bcdc1e56ed926f36dd52 to your computer and use it in GitHub Desktop.
Line chart with tooltip
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
<!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"> &#10095;</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