I am trying to create a X-value mouseoever event for all the valuelines in my line chart. However, I couldn't get each line highlighted and right now I have only one line with mouseover effect.
I would like to create a combination of this(http://bl.ocks.org/mbostock/8033015) and this (http://bl.ocks.org/mbostock/3902569). I have been struggling with this for days, any help would be greatly appreciated!!!
Here is my original code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Language" content="en">
<title>Energy Production in California</title>
<style>
body { font: 14px avenir next;}
path {
stroke: #e5e5e5;
stroke-width: ;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
/*.sources {
font-size:14px;
fill:#e5e5e5;
}
.sources:hover {
/* font-size:18px;*/
font-weight: 800;
fill:#853b62;
}*/
/*.graph-svg-component {
background-color:#e1e1e1;
}*/
div {
padding: 15px;
width:800px;
margin-left:auto;
margin-right:auto;
border:10px ;
}
.overlay {
fill: none;
pointer-events: all;
}
.focus circle {
fill: steelblue;
stroke: steelblue;
}
.line {
stroke: #e5e5e5;
/* stroke:white;*/
}
.line:hover {
stroke: #e769ab ;
stroke-width:2;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<div>
<script>
var margin = {top: 50, right: 140, bottom: 50, left: 80},
width = 1000 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y").parse,
bisectDate = d3.bisector(function(d) { return d.date; }).left,
formatValue = d3.format(",.2f");
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(15);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(10);
var voronoi = d3.geom.voronoi()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.total_hydroelectric); })
.clipExtent([[-margin.left, -margin.top], [width + margin.right, height + margin.bottom]]);
// var valueline1 = d3.svg.line()
// .x(function(d) { return x(d.date); })
// .y(function(d) { return y(d.california_energy_production); });
var valueline2 = d3.svg.line()
// .interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.total_hydroelectric); });
var valueline3 = d3.svg.line()
// .interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.nuclear); });
var valueline4 = d3.svg.line()
// .interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.in_state_coal); });
var valueline5 = d3.svg.line()
// .interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.oil); });
var valueline6 = d3.svg.line()
// .interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.natural_gas ); });
var valueline7 = d3.svg.line()
// .interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.geothermal ); });
var valueline8 = d3.svg.line()
// .interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.biomass ); });
var valueline9 = d3.svg.line()
// .interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.wind ); });
var valueline10 = d3.svg.line()
// .interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.solar ); });
var valueline11 = d3.svg.line()
// .interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.other ); });
var valueline12 = d3.svg.line()
// .interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.direct_coal_imports ); });
var valueline13 = d3.svg.line()
// .interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.other_imports ); });
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("class", "graph-svg-component")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Get the data
d3.csv("data_3.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
// d.california_energy_production = +d.california_energy_production;
d.total_hydroelectric = +d.total_hydroelectric;
d.nuclear = +d.nuclear;
d.in_state_coal = +d.in_state_coal;
d.oil = +d.oil;
d.natural_gas = +d.natural_gas;
d.geothermal = +d.geothermal;
d.biomass = +d.biomass;
d.wind = +d.wind;
d.solar = +d.solar;
d.other = +d.other;
d.direct_coal_imports = +d.direct_coal_imports;
d.other_imports = +d.other_imports;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return Math.max( d.total_hydroelectric, d.nuclear,d.in_state_coal, d.oil, d.natural_gas, d.geothermal, d.biomass, d.wind, d.solar, d.other, d.direct_coal_imports, d.other_imports); })]);
// Add the valueline path.
// svg.append("path")
// .attr("class", "line")
// .attr("d", valueline1(data));
svg.append("path")
.attr("class", "line")
.attr("d", valueline2(data));
svg.append("path")
.attr("class", "line")
.attr("d", valueline3(data));
svg.append("path")
.attr("class", "line")
.attr("d", valueline4(data));
svg.append("path")
.attr("class", "line")
.attr("d", valueline5(data));
svg.append("path")
.attr("class", "line")
.attr("d", valueline6(data));
svg.append("path")
.attr("class", "line")
.attr("d", valueline7(data));
svg.append("path")
.attr("class", "line")
.attr("d", valueline8(data));
svg.append("path")
.attr("class", "line")
.attr("d", valueline9(data));
svg.append("path")
.attr("class", "line")
.attr("d", valueline10(data));
svg.append("path")
.attr("class", "line")
.attr("d", valueline11(data));
svg.append("path")
.attr("class", "line")
.attr("d", valueline12(data));
svg.append("path")
.attr("class", "line")
.attr("d", valueline13(data));
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
var focus = svg.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("circle")
.attr("r", 4.5);
focus.append("text")
.attr("x", 9)
.attr("dy", ".35em");
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.date) + "," + y(d.total_hydroelectric) + ")");
focus.select("text").text(d.total_hydroelectric + " Gigawatt/Hours");
};
svg.append("text")
// .attr("class", "sources")
.attr("transform", "translate(" + (width+3) + "," + y(data[30].total_hydroelectric) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "#898989")
.text("Total Hydroelectric");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[30].nuclear) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "#898989")
.text("Nuclear");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[30].in_state_coal) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "#898989")
.text("In State Coal");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[30].oil) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "#898989")
.text("Oil");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[30].natural_gas) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "#898989")
.text("Natural Gas");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[30].geothermal) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "#898989")
.text("Geotheral");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[30].biomass) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "#898989")
.text("Biomass");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[30].wind) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "#898989")
.text("Wind");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[30].solar) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "#898989")
.text("Solar");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[30].other) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "#898989")
.text("Other");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[30].direct_coal_imports) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "#898989")
.text("Direct Coal Imports");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[30].other_imports) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "#898989")
.text("Other Imports");
});
</script>
</div>
</body>
</html>
and here is my data.
date,california_energy_production,total_hydroelectric,nuclear,in_state_coal,oil,natural_gas,geothermal,biomass,wind,solar,other,direct_coal_imports,other_imports
1983,199609,59351,6738,563,6535,45486,7020,731,52,2,0,17001,56130
1984,211900,46880,13467,731,2632,58248,9272,1099,192,11,0,18080,61288
1985,210172,33898,18911,865,2790,69771,10957,1171,655,33,0,14112,57009
1985,211028,44478,28000,1033,3126,49260,13094,2063,1221,64,6,17588,51095
1987,220371,27140,32995,1163,2143,75437,14083,2461,1713,188,5,17544,45499
1988,232926,26692,35481,1791,8158,74221,14194,4092,1824,315,4,19243,46911
1989,238567,32742,33803,2479,9275,78916,15247,5204,2139,471,4,17223,41064
1990,252355,26092,36586,3692,4449,76082,16038,6644,2418,681,4,17710,61959
1991,242343,23244,37167,3050,523,75828,15566,7312,2669,719,0,20392,55873
1992,245535,22373,38622,3629,107,87032,16491,7362,2707,700,2,28806,37704
1993,242026,41595,36579,2549,2085,70715,15770,5760,2867,857,0,20358,42892
1994,256719,25626,38828,2655,1954,95025,15573,7173,3293,798,0,22440,43354
1995,256367,51665,36186,1136,489,78378,14267,5969,3182,793,0,16788,47514
1996,253621,47883,39753,2870,693,66711,13539,5557,3154,832,343,22590,49696
1997,230243,41400,37267,2276,143,74341,11950,5701,2739,810,896,22411,30310
1998,244577,48757,41715,2701,123,82052,12554,5266,2776,839,230,22570,24993
1999,243077,41627,40419,3602,55,84703,13251,5663,3433,838,0,22802,26685
2000,246876,42053,43533,3183,449,106878,13456,6086,3604,860,0,23877,2897
2001,267399,24988,33294,4041,379,116369,13525,5761,3242,836,38,23699,41227
2002,274444,31359,34353,4275,87,92752,13396,6196,3546,851,35,23653,63941
2003,280026,36341,35594,4269,103,94715,13329,6092,3316,759,108,23148,62253
2004,290211,34490,30241,4086,127,105358,13494,6080,4258,741,48,24504,66785
2005,289177,40263,36155,4283,148,97110,13292,6076,4084,660,24,24114,62967
2006,298454,48559,32036,4190,134,109316,13093,5861,4902,616,34,14452,65263
2007,304823,27105,35698,4217,103,120459,13029,5743,5570,668,15,14417,77799
2008,307448,24460,32482,3977,92,123036,12907,5927,5724,733,39,14463,83608
2009,299101,29220,31509,3735,67,117277,12907,6096,6249,851,20,13556,77615
2010,291310,34327,32214,3406,52,109916,12740,5960,6172,912,12,13119,72481
2011,293875,42731,36666,3120,36,91276,12685,5986,7598,1097,13,13032,79633
2012,302113,27459,18491,1580,90,121761,12733,6121,9242,1834,14,9716,93071
2013,296569,24098,17860,1018,38,120896,12485,6466,12694,4154,14,11824,85022
The key here is making the voronoi work correctly; after that it all falls into place. The way you started only used the total_hydroelectric data, but it needs to take into account all your data. The quickest way to do that, it to create a flat data structure of all the data:
var flatData = [];
data.forEach(function(d) {
d.date = parseDate(d.date);
// d.california_energy_production = +d.california_energy_production;
d.total_hydroelectric = +d.total_hydroelectric;
d.nuclear = +d.nuclear;
d.in_state_coal = +d.in_state_coal;
...
flatData.push({date: d.date, value: d.total_hydroelectric, key: "total_hydroelectric"});
flatData.push({date: d.date, value: d.nuclear, key: "nuclear"});
flatData.push({date: d.date, value: d.in_state_coal, key: "in_state_coal"});
...
});
Later:
voronoiGroup.selectAll("path")
.data(voronoi(flatData))
.enter().append("path")
.attr("d", function(d) { return "M" + d.join("L") + "Z"; })
.datum(function(d) { return d.point; })
.on("mouseover", mouseover)
.on("mouseout", mouseout);
And when you draw your lines add a class so you can find them later:
svg.append("path")
.attr("class", "line total_hydroelectric") // tag this as hydro
.attr("d", valueline2(data));
Where mouseover/mouseout then becomes:
function mouseover(d) {
d3.select("."+d.key).classed("line-hover", true);
focus.attr("transform", "translate(" + x(d.date) + "," + y(d.value) + ")");
focus.select("text").text(d.date);
}
function mouseout(d) {
d3.select("."+d.key).classed("line-hover", false);
focus.attr("transform", "translate(-100,-100)");
}
In this way then the voronoi events also take care of your x-value point hover.
Here's an example putting it together.