D3 mouseover effect for each chart on one page

I have multiple d3-charts on one page and would like to add a mouseover effect for each chart. At the moment only one chart is affected and has a mouseover effect.

I've created an example with multiple charts. Here is the fiddle: http://jsfiddle.net/zumdpjzx/

for( var i= 1; i < 3; i++){

console.log(i);

var arrData = [
                ["2014-08-20", 100, 100],
                ["2014-08-21", 95, 85],
                ["2014-08-22", 93, 71],
                ["2014-08-23", 88, 57],
                ["2014-08-24", 86, 42],
                ["2014-08-25", 98, 28],
                ["2014-08-26", 117, 14],
                ["2014-08-27", 123, 0]
              ];



arrData = arrData.sort((function(index){
return function(a, b){
    return (a[index] === b[index] ? 0 : (a[index] < b[index] ? -1 : 1));
};
})(0));

 console.log("array: " + arrData);


var margin = {top: 40, right: 40, bottom: 60, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;

var parseDate = d3.time.format("%Y-%m-%d").parse;


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(arrData.length)
                  .tickFormat(d3.time.format("%Y-%m-%d"));

var yAxis = d3.svg.axis()
              .scale(y)
              .orient("left");

var line = d3.svg.line()
             .x(function(d) { return x(d.date); })
             .y(function(d) { return y(d.close); });

 var line2 = d3.svg.line()
               .x(function(d) { return x(d.date); })
               .y(function(d) { return y(d.open); });

 var svg = d3.select("#chart" + i).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 data = arrData.map(function(d) {
                          return {
                             //date: d[0],
                             date: parseDate(d[0]),
                             close: d[2],
                             open: d[1]
                          };

                       });

var length = arrData.length - 1;

// 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.close, d.open); })]);

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", ".15em")
               .attr("transform", function(d) {
                     return "rotate(-65)" 
                     });

svg.append("g")
  .attr("class", "y axis")
  .call(yAxis)
  .append("text")
  .attr("transform", "rotate(-90)")
  .attr("y", 6)
  .attr("dy", ".71em")
  .style("text-anchor", "end")
  .text("Open Issues");

svg.append("path")
  .datum(data)
  .attr("class", "line")
  .attr("d", line);

svg.append("path")    // Add the valueline2 path.
.attr("class", "line")
.style("stroke", "red")
.attr("d", line2(data))
.text("line2");

svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[length].open) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "red")
.text("Open");

svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[length].close) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "steelblue")
.text("Close");

//mouse over
var focus = svg.append("g")
  .attr("class", "focus")
  .style("display", "none");

focus.append("circle")
  .attr("r", 4.5);
  focus.append("circle")
  .attr("r", 4.5);

var bisectDate = d3.bisector(function(d) { return d.date; }).left;
var formatValue = d3.format(",.2f");
var formatCurrency = function(d) { return  + d; };


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", mousemoveOpen);





}

function mousemoveOpen() {
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.open) + ")");
focus.select("text").text(formatCurrency(d.open));
}

Edit:

I've found now a new solution. Here is the fiddle: http://jsfiddle.net/4h72u83h/1/ Thank you for your help!

mousemoveOpen is called on both charts in your example. Looking at it, 'focus' and 'data' exist outside of the closure. By the time mousemoveOpen gets called both will be fetched from the global scope and use the last value they were set to. That's why the last chart always gets updated: the focus and data variables point reference the last chart.

I tried playing with your fiddle example, but I couldn't get it working. You could use underscore, or native javascript's 'bind'

You're pretty close to the mark, but you're not keeping track of which focus element you are updating in the mouseout, mouseover and mousemove handlers.

You could do something like this:

for (var i = 1; i < 3; i++) {

  console.log(i);

  var arrData = [
    ["2014-08-20", 100, 100],
    ["2014-08-21", 95, 85],
    ["2014-08-22", 93, 71],
    ["2014-08-23", 88, 57],
    ["2014-08-24", 86, 42],
    ["2014-08-25", 98, 28],
    ["2014-08-26", 117, 14],
    ["2014-08-27", 123, 0]
  ];



  arrData = arrData.sort((function(index) {
    return function(a, b) {
      return (a[index] === b[index] ? 0 : (a[index] < b[index] ? -1 : 1));
    };
  })(0));

  console.log("array: " + arrData);


  var margin = {
      top: 40,
      right: 40,
      bottom: 60,
      left: 50
    },
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

  var parseDate = d3.time.format("%Y-%m-%d").parse;


  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(arrData.length).tickFormat(d3.time.format("%Y-%m-%d"));

  var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

  var line = d3.svg.line()
    .x(function(d) {
      return x(d.date);
    })
    .y(function(d) {
      return y(d.close);
    });

  var line2 = d3.svg.line()
    .x(function(d) {
      return x(d.date);
    })
    .y(function(d) {
      return y(d.open);
    });

  var svg = d3.select("#chart" + i).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 data = arrData.map(function(d) {
    return {
      //date: d[0],
      date: parseDate(d[0]),
      close: d[2],
      open: d[1]
    };

  });

  console.log(data);
  console.log(arrData.length);
  var length = arrData.length - 1;

  // 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.close, d.open);
  })]);

  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", ".15em")
    .attr("transform", function(d) {
      return "rotate(-65)"
    });

  svg.append("g")
    .attr("class", "y axis")
    .call(yAxis)
    .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Open Issues");

  svg.append("path")
    .datum(data)
    .attr("class", "line")
    .attr("d", line);

  svg.append("path") // Add the valueline2 path.
    .attr("class", "line")
    .style("stroke", "red")
    .attr("d", line2(data))
    .text("line2");

  svg.append("text")
    .attr("transform", "translate(" + (width + 3) + "," + y(data[length].open) + ")")
    .attr("dy", ".35em")
    .attr("text-anchor", "start")
    .style("fill", "red")
    .text("Open");

  svg.append("text")
    .attr("transform", "translate(" + (width + 3) + "," + y(data[length].close) + ")")
    .attr("dy", ".35em")
    .attr("text-anchor", "start")
    .style("fill", "steelblue")
    .text("Close");

  //mouse over
  var focus = svg.append("g")
    .attr("class", "focus")
    .style("display", "none");

  focus.append("circle")
    .attr("r", 4.5);
  focus.append("circle")
    .attr("r", 4.5);

  var bisectDate = d3.bisector(function(d) {
    return d.date;
  }).left;
  var formatValue = d3.format(",.2f");
  var formatCurrency = function(d) {
    return +d;
  };


  focus.append("text")
    .attr("x", 9)
    .attr("dy", ".35em");

  svg.append("rect")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height)
    .on("mouseover", function() {
      var thisFocus = d3.select(d3.select(this)[0][0].parentNode).select(".focus");
      thisFocus.style("display", null);
    })
    .on("mouseout", function() {
      var thisFocus = d3.select(d3.select(this)[0][0].parentNode).select(".focus");
      thisFocus.style("display", "none");
    })
    .on("mousemove", mousemoveOpen);
}

function mousemoveOpen() {
  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;
  var thisFocus = d3.select(d3.select(this)[0][0].parentNode).select(".focus");
  thisFocus.attr("transform", "translate(" + x(d.date) + "," + y(d.open) + ")");
  thisFocus.select("text").text(formatCurrency(d.open));
}
body {
  font: 10px sans-serif;
}
.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}
.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 1.5px;
}
.overlay {
  fill: none;
  pointer-events: all;
}
.focus circle {
  fill: none;
  stroke: steelblue;
}
.legend {
  padding: 5px;
  font: 10px sans-serif;
  background: yellow;
  box-shadow: 2px 2px 1px #888;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

<body>
  <div id="chart1"></div>
  <div id="chart2"></div>
</body>

Basically, what I've done there is to modify the mouseover, mouseout and mousemove, so that it grabs the right focus element to be updated and then updates it.

The important bit is:

var thisFocus = d3.select(d3.select(this)[0][0].parentNode).select(".focus");
thisFocus.attr("transform", "translate(" + x(d.date) + "," + y(d.open) + ")");
thisFocus.select("text").text(formatCurrency(d.open));

The first line grabs the focus that corresponds to the graph that is receiving the mouse events. You'll see similar lines in the mouseover and mouseout handlers.

I would probably recommend that you just keep track of the focus elements separately in an object to begin with, and then you can just use that reference in your mouse handling functions. Selecting it all the time can have performance impacts, though not so much in this case.