title: Mouseover issues in D3.js, containing Path elements and changing tooltip data once focus is brushed?
body: <p>I'm having the following issues with a Multi Series, Focus + Context D3 chart and thought I'd try to get the answers to my main questions in one go.</p>
  
  <p>The questions I have are as follows:</p>
  
  <ol>
  <li><p>How do I ensure the tooltips I've created are not affected by the hair-line (which follows the mouse) of the chart i.e. do not disappear and instead persist until the mouse is moved? This is how they would behave if the hair-line was not visible or had been created.</p></li>
  <li><p>For some reason (probably obvious but not to me) the paths of data are not constrained to the focus area and spill out to the left of the chart. How does one solve this? I realise it's something to do with the left margin, probably.</p></li>
  <li><p>Lastly when the data is brushed, the focus chart updates via the context but I'm struggling to update the tooltip data. Can anyone see what's wrong with the existing code?</p></li>
  </ol>
  
  <p>The CSS:</p>
  
  <pre><code>        body {
            font: 10px sans-serif;
          }
  
          .axis path,
          .axis path_steelblue {
              fill: none;
              stroke: #000;
              shape-rendering: geometricPrecision;
          }
  
          .path_steelblue {
              fill: none;
              stroke: steelblue;
              stroke-width: 1.5px;
          }
  
          .path_blue {
              fill: none;
              stroke: #3333FF;
              stroke-width: 1.5px;
          }
          .path_red {
              fill: none;
              stroke:red;
              stroke-width: 1.5px;
          }
          .path_yellow {
              fill: none;
              stroke:yellow;
              stroke-width: 1.5px;
          }
  
          .brush .extent {
              stroke: #FFF;
              fill-opacity: .125;
              shape-rendering: crispEdges;
          }
  
          .axis path,
          .axis line {
              fill: none;         
              stroke: grey;       
              stroke-width: 1;     
              shape-rendering: crispEdges;
          }
          .grid .tick {
              stroke: lightgrey;
              opacity: 0.7;
              shape-rendering: crispEdges;
          }
          .grid path {
              stroke-width: 0;
              shape-rendering: crispEdges;
          }
  
          .dot {
              fill: white;
              stroke: white;
              opacity:0.01;
              stroke-width: 0px;
              cursor:crosshair;
          }
  
          div.tooltip {   
              position: absolute;           
              text-align: left;           
              width: 120px;                  
              height: 48px;                 
              padding: 10px;             
              font: 12px sans-serif;        
              background: steelblue;   
              border: 0px;      
              border-radius: 8px;           
              pointer-events: none;
          }
  
          div.tooltipSolvency {       
              background: #3333FF;
          }
  
          div.tooltipTechnical {       
              background: red;
          }
  
          div.tooltipAccounting {       
              background: yellow;
          }
  
          .hover-line { 
              stroke: #000;
              fill: none;
              stroke-width: 1px;
              left: 10px;
              shape-rendering: crispEdges;
          }
  
          .hover-text {
              stroke: none;
              font-size: 12px;
              font-weight: bold;
              fill: #000000;
          }
  </code></pre>
  
  <p>The D3 code:</p>
  
  <pre><code>        var margin = { top: 10, right: 10, bottom: 100, left: 40 },
              margin2 = { top: 430, right: 10, bottom: 20, left: 40 },
              width = 960 - margin.left - margin.right,
              height = 500 - margin.top - margin.bottom,
              height2 = 500 - margin2.top - margin2.bottom;
  
          var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S").parse;
          var formatTime = d3.time.format("%d/%m/%Y");
  
          var x = d3.time.scale().range([0, width]),
              x2 = d3.time.scale().range([0, width]),
              y = d3.scale.linear().range([height, 0]),
              y2 = d3.scale.linear().range([height2, 0]);
  
          var xAxis = d3.svg.axis().scale(x).orient("bottom"),
              xAxis2 = d3.svg.axis().scale(x2).orient("bottom"),
              yAxis = d3.svg.axis().scale(y).orient("left");
  
          var brush = d3.svg.brush()
              .x(x2)
              .on("brush", brushed);
  
          var line = d3.svg.line()
              .x(function(d) { return x(d.date); })
              .y(function(d) { return y(d.chartValue); });
  
          var line2 = d3.svg.line()
              .x(function(d) { return x2(d.date); })
              .y(function(d) { return y2(d.chartValue); });
  
          var svg = d3.select(".tracker-chart").append("svg")
              .attr("width", width + margin.left + margin.right)
              .attr("height", height + margin.top + margin.bottom);
              //.style("margin-left", "40px")
              //.style("margin-top", "40px");
  
          //var container = svg.append("g")
          //  .attr("width", width + margin.left + margin.right)
          //  .attr("height", height + margin.top + margin.bottom);
  
          svg.append("defs").append("clipPath")
              .attr("id", "clip")
            .append("rect")
              .attr("width", width)
              .attr("height", height);
  
          var focus = svg.append("g")
              .attr("class", "focus")
              .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  
          // Hover line. 
          var hoverLineGroup = svg.append("g") //svg.append("g") -- Ensures hairline follows mouse pointer put interferes with the tooltip not allowing it to persist until the mouse moves again
              .attr("class", "hover-line");
  
          var hoverLine = hoverLineGroup
              .append("line")
                  .attr("x1", 10).attr("x2", 10) 
                  .attr("y1", 0).attr("y2", height + 10);
  
          var hoverDate = hoverLineGroup.append('text')
              .attr("class", "hover-text")
              .attr('y', height - (height-10));
  
          // Hide hover line by default.
          hoverLine.style("opacity", 1e-6);
  
          var div = d3.select(".tracker-chart").append("div")
              .attr("class", "tooltip")
              .style("opacity", 0);
  
          var divSolvency = d3.select(".tracker-chart").append("div")
              .attr("class", "tooltip tooltipSolvency")
              .style("opacity", 0);
  
          var divTechnical = d3.select(".tracker-chart").append("div")
              .attr("class", "tooltip tooltipTechnical")
              .style("opacity", 0);
  
          var divAccounting = d3.select(".tracker-chart").append("div")
              .attr("class", "tooltip tooltipAccounting")
              .style("opacity", 0);
  
          var context = svg.append("g")
              .attr("class", "context")
              .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
  
          d3.xml("data.xml", "application/xml", function(error, data) {
              var assetResultsData = d3.select(data).select("AssetResults").selectAll("Result");
              assetResultsData[0].forEach(crunch);
  
              var liabilityResultsDataSol = d3.select(data).selectAll("LiabilityResults Liability[name='Solvency'] Results Item");
              liabilityResultsDataSol[0].forEach(crunch);
  
              var liabilityResultsDataTP = d3.select(data).selectAll("LiabilityResults Liability[name='Technical provisions'] Results Item");
              liabilityResultsDataTP[0].forEach(crunch);
  
              var liabilityResultsDataTV = d3.select(data).selectAll("LiabilityResults Liability[name='Accounting'] Results Item");
              liabilityResultsDataTV[0].forEach(crunch);
  
              x.domain(d3.extent(assetResultsData[0], function (d) { return d.date; }));
              y.domain([450, 600]); //Hard coded for demo purposes
              //y.domain(d3.extent(assetResultsData[0], function (d) { return d.chartValue; }));
              x2.domain(x.domain());
              y2.domain(y.domain());
  
              focus.append("path")
                  .datum(assetResultsData[0])
                  .attr("class", "path_steelblue")
                  .attr("d", line);
  
              focus.append("path")
                  .datum(liabilityResultsDataSol[0])
                  .attr("class", "path_blue")
                  .attr("d", line);
  
              focus.append("path")
                  .datum(liabilityResultsDataTV[0])
                  .attr("class", "path_red")
                  .attr("d", line);
  
              focus.append("path")
                  .datum(liabilityResultsDataTP[0])
                  .attr("class", "path_yellow")
                  .attr("d", line);
  
              focus.append("g")
                  .attr("class", "x axis")
                  .attr("transform", "translate(0," + height + ")")
                  .call(xAxis);
  
              focus.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("Millions ($)");
  
              focus.append("g")
                  .attr("class", "grid")
                  .call(make_y_axis()
                      .tickSize(-width, 0, 0)
                      .tickFormat("")
                  );
  
              context.append("path")
                  .datum(assetResultsData[0])
                  .attr("class", "path_steelblue")
                  .attr("d", line2);
  
              context.append("path")
                  .datum(liabilityResultsDataSol[0])
                  .attr("class", "path_blue")
                  .attr("d", line2);
  
              context.append("path")
                  .datum(liabilityResultsDataTV[0])
                  .attr("class", "path_yellow")
                  .attr("d", line2);
  
              context.append("path")
                  .datum(liabilityResultsDataTP[0])
                  .attr("class", "path_red")
                  .attr("d", line2);
  
              context.append("g")
                  .attr("class", "x axis")
                  .attr("transform", "translate(0," + height2 + ")")
                  .call(xAxis2);
  
              context.append("g")
                  .attr("class", "x brush")
                  .call(brush)
              .selectAll("rect")
                  .attr("y", -6)
                  .attr("height", height2 + 7);
  
              addTooltip(div, assetResultsData[0], "Assets");
              addTooltip(divSolvency, liabilityResultsDataSol[0], "Solvency");
              addTooltip(divTechnical, liabilityResultsDataTV[0], "Technical Provisions");
              addTooltip(divAccounting, liabilityResultsDataTP[0], "Accounting");
          });
  
          // Add mouseover events for hover line.
          d3.select(".tracker-chart").on("mouseover", function() {
              }).on("mousemove", function() {
                      //console.log('mousemove', d3.mouse(this));
                      var mouse_x = d3.mouse(this)[0];
                      var mouse_y = d3.mouse(this)[1];
                      var graph_y = y.invert(mouse_y);
                      var graph_x = x.invert(mouse_x);
                      //console.log(graph_x);
                      var format = d3.time.format('%a %b %d %Y');
                      hoverDate.text(format(graph_x));
                      hoverDate.attr('x', mouse_x);
                      //console.log(x.invert(mouse_x));
                      hoverLine.attr("x1", mouse_x).attr("x2", mouse_x)
                      hoverLine.style("opacity", 1);
                  }).on("mouseout", function() {
                      //hoverLine.style("opacity", 1e-6);
                  });
  
          function addTooltip(div, data, label) {
              focus.selectAll("dot")
                      .data(data)
                  .enter().append("circle")
                      .attr("class", "dot")
                      .attr("r", 5)
                      .attr("cx", function (d) { return x(d.date); })
                      .attr("cy", function (d) { return y(d.chartValue); })
                          .on("mouseover", function(d) {
                              div.transition()
                                  .duration(50)
                                  .style("opacity", .9);
                              div.html(label + "&lt;br /&gt;" + formatTime(d.date) + "&lt;br /&gt;" + "$" + (d.chartValue).toFixed(3) + " Million")
                                  .style("left", (d3.event.pageX) + "px")
                                  .style("top", (d3.event.pageY - 28) + "px");
  
                      }).on("mouseout", function(d) {
                          div.transition()
                              .duration(200)
                              .style("opacity", 0);
                      });
                      /*
              focus.on('mouseover', function(){
                  brush_elm = focus.select("circle").node();
                  console.log(brush_elm);
                  console.log(this);
                  new_click_event = new Event('mouseover');
                  new_click_event.pageX = d3.event.pageX;
                  new_click_event.clientX = d3.event.clientX;
                  new_click_event.pageY = d3.event.pageY;
                  new_click_event.clientY = d3.event.clientY;
                  brush_elm.dispatchEvent(new_click_event);
              });*/
          }
  
          function brushed() {
              x.domain(brush.empty() ? x2.domain() : brush.extent());
              focus.select(".path_steelblue").attr("d", line);
              focus.select(".path_blue").attr("d", line);
              focus.select(".path_yellow").attr("d", line);
              focus.select(".path_red").attr("d", line);
              focus.select(".x.axis").call(xAxis);
          }
  
          function make_y_axis() {        
              return d3.svg.axis()
                  .scale(y)
                  .orient("left")
                  .ticks(5)
          }
  
          function crunch(d) {
              d.date = parseDate(d.getAttribute("date"));
              d.chartValue = +d.getAttribute("value")/ 1000000;
              d.hoverText = +d.getAttribute("value");
          }
  </code></pre>
  
  <p><strong>EDIT</strong></p>
  
  <p>Image included: <img src="https://i.stack.imgur.com/WuYof.jpg" alt="Multi Series Focus + Context Graph"></p>
  
  <p><strong>NEW EDIT</strong></p>
  
  <pre><code>    // Hover line. 
          var hoverLineGroup = focus.append("g") //svg.append("g") -- Ensures hairline follows mouse pointer put interferes with the tooltip not allowing it to persist until the mouse moves again
              .attr("class", "hover-line");
  </code></pre>