I know this is a simple problem - something similar was solved in these questions: Multiseries line chart with mouseover tooltip and How to as mouseover to Line Graph Interactive in D3 . I tried to use the code from the first situation for a similar visualization. The original datafile is in csv, in a format like
date m_total_calories rhr
20160101 2792 72
20160102 2047 65
20160103 2201 62
20160104 2624 67
20160105 2169 64
20160106 3011 59
20160107 1893 62
20160108 3035 56
20160109 1946 57
In my visualization, I'm using 2 axes, so I have to define the variables in a slightly different way. As you can see below, I have variables line_l and line_r that are visualized, and they should be referred to by the object "lines". I tried defining it as an array, but it didn't work. Do you know what could the solution be?
(I apologize for asking such a simple question, I'm new to JavaScript.)
<html>
<head>
<title> Data from Jawbone UP3 </title>
</head>
<body>
<h1> <center> Data collected by Jawbone UP3 </center> </h1>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="d3.tip"></script>
<script>
function myFunction() {
d3.csv('2016_up.csv', draw);}
function draw(data) {
"use strict";
var margin = 60,
width = 950 - margin,
height = 475 - margin;
var parseDate = d3.time.format("%Y%m%d").parse;
data.forEach(function(d) {
d.date = parseDate(d.date);
d.m_total_calories = +d.m_total_calories;
d.rhr = +d.rhr;
});
var x_scale = d3.time.scale()
.domain(d3.extent(data,function(d){return d.date}))
.range([margin, width]);
var y_left_scale = d3.scale.linear()
.domain(d3.extent(data,function(d){return d.m_total_calories}))
.range([height, margin]);
var y_right_scale = d3.scale.linear()
.domain(d3.extent(data,function(d){return d.rhr}))
.range([height, margin]);
var x_axis = d3.svg.axis()
.scale(x_scale)
.orient("bottom");
var y_axis_left = d3.svg.axis()
.scale(y_left_scale)
.orient("left");
var y_axis_right = d3.svg.axis()
.scale(y_right_scale)
.orient("right");
var line_l = d3.svg.line()
.x(function(d){return x_scale(d.date);})
.y(function(d){return y_left_scale(d.m_total_calories);});
var line_r = d3.svg.line()
.x(function(d){return x_scale(d.date);})
.y(function(d){return y_right_scale(d.rhr);});
d3.select("body")
.append("svg")
.attr("width", width+margin)
.attr("height", height+margin)
.append('g')
.attr("transform","translate(" + margin + "," + margin + ")");
d3.select('svg')
.append('path')
.datum(data)
.attr('d', line_l)
.style("stroke", "steelblue")
.style("fill", "none");
d3.select('svg')
.append('path')
.datum(data)
.attr('d', line_r)
.style("stroke", "red")
.style("fill", "none");
d3.select("svg")
.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(x_axis);
d3.select("svg")
.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin + ",0)")
.style("fill", "steelblue")
.call(y_axis_left)
.append("text")
.attr("y", 30)
.attr("dy", ".71em")
.style("text-anchor", "middle")
.text("Calories burned")
d3.select("svg")
.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + " ,0)")
.style("fill", "red")
.call(y_axis_right)
.append("text")
.attr("y", 30)
.attr("dy", ".71em")
.style("text-anchor", "middle")
.text("Average heart rate")
;
// var mouseG = svg.append("g")
var mouseG = d3.select("svg").append("g")
.attr("class", "mouse-over-effects");
mouseG.append("path") // this is the black vertical line to follow mouse
.attr("class", "mouse-line")
.style("stroke", "black")
.style("stroke-width", "1px")
.style("opacity", "0");
// THIS IS PROBLEMATIC
var lines = document.getElementsByClassName('line');
var mousePerLine = mouseG.selectAll('.mouse-per-line')
.data(data) // cities)
.enter()
.append("g")
.attr("class", "mouse-per-line");
mousePerLine.append("circle")
.attr("r", 7)
.style("fill", "none")
.style("stroke-width", "1px")
.style("opacity", "0");
mousePerLine.append("text")
.attr("transform", "translate(10,3)");
mouseG.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', width) // can't catch mouse events on a g element
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', function() { // on mouse out hide line, circles and text
d3.select(".mouse-line")
.style("opacity", "0");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "0");
d3.selectAll(".mouse-per-line text")
.style("opacity", "0");
})
.on('mouseover', function() { // on mouse in show line, circles and text
d3.select(".mouse-line")
.style("opacity", "1");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "1");
d3.selectAll(".mouse-per-line text")
.style("opacity", "1");
})
.on('mousemove', function() { // mouse moving over canvas
var mouse = d3.mouse(this);
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
d3.selectAll(".mouse-per-line")
.attr("transform", function(d, i) {
console.log(width/mouse[0])
var xDate = x_scale.invert(mouse[0]), // x.invert(mouse[0]),
bisect = d3.bisector(function(data) { return data.date; }).right, //d) { return d.date; }).right;
idx = bisect(data.values, xDate);
var beginning = 0,
// HERE COMES A PROBLEM WITH LINES DEFINITION
end = lines[i].getTotalLength(),
end = 100,
target = null;
while (true){
target = Math.floor((beginning + end) / 2);
// HERE COMES ANOTHER PROBLEM WITH LINES DEFINITION
pos = lines[i].getPointAtLength(target);
if ((target === end || target === beginning) && pos.x !== mouse[0]) {
break;
}
if (pos.x > mouse[0]) end = target;
else if (pos.x < mouse[0]) beginning = target;
else break; //position found
}
d3.select(this).select('text')
.text(y.invert(pos.y).toFixed(2));
return "translate(" + mouse[0] + "," + pos.y +")";
});
});
</script>
<button onclick="myFunction()"> Show me the Graph! </button>
</body>
</html>
I would be really grateful if someone could give me some advice / help me find a solution. Thank you very much in advance!