Search code examples
d3.jsonmouseoverbrush

d3 brushing and mouse move coexist


im trying to update http://bl.ocks.org/d3noob/6eb506b129f585ce5c8a and add brushing into it(brushing displayed under the line graph) to make it look like https://www.google.com.hk/#q=s%26p+500

added coded to the first link:

var brush = d3.svg.brush()
  .x(x)
  .on("brush", brushmove)
  .on("brushend", brushend);

svg.append("g")
  .attr("class", "brush")
  .call(brush)
  .selectAll('rect')
    .attr('height', height);

function brushmove() {
  var extent = brush.extent();
}

function brushend() {
 x.domain(brush.extent())
 console.log(brush.extent());
}

The problem is that once i add brushing into it, there's a background formed behind the graph and i can't perform mouse events(mousemove) anymore.

Is there a way to fix it to make it look like google? 1) brushing and mouse event coexist 2) brushing area under the curve

var csv = date,close1, close2 26-Mar-12,606.98,58.13 27-Mar-12,614.48,53.98 28-Mar-12,617.62,67.00 29-Mar-12,609.86,89.70 30-Mar-12,599.55,99.00 2-Apr-12,618.63,130.28 3-Apr-12,629.32,166.70 4-Apr-12,624.31,234.98 5-Apr-12,633.68,345.44 9-Apr-12,636.23,443.34 10-Apr-12,628.44,543.70 11-Apr-12,626.20,580.13 12-Apr-12,622.77,605.23 13-Apr-12,605.23,626.20 16-Apr-12,580.13,628.44 17-Apr-12,543.70,636.23 18-Apr-12,443.34,633.68 19-Apr-12,345.44,624.31 20-Apr-12,234.98,629.32 23-Apr-12,166.70,618.63 24-Apr-12,130.28,599.55 25-Apr-12,99.00,609.86 26-Apr-12,89.70,617.62 27-Apr-12,67.00,614.48 30-Apr-12,53.98,606.98 1-May-12,58.13,503.15


Solution

  • Both the example you link to and the brush add a rect on top of the plot to capture mouse events. The key to making them coexist is to add the brush (and allow it to create its rect) and then use that rect to add the tooltip events. This way you only end up with one point-events rect:

    // add a g for the brush
    var context  = svg.append("g");
    
    // add the brush
    context.call(brush);
    
    // grab the brush's rect and add the tooltip events
    context.select(".background")
      .on("mouseover", function() {
        focus.style("display", null);
      })
      .on("mouseout", function() {
        focus.style("display", "none");
      })
      .on("mousemove", mousemove);
    

    Full code:

    <!DOCTYPE html>
    <meta charset="utf-8">
    <style>
      /* set the CSS */
      
      body {
        font: 12px Arial;
      }
      
      path {
        stroke: steelblue;
        stroke-width: 2;
        fill: none;
      }
      
      .axis path,
      .axis line {
        fill: none;
        stroke: grey;
        stroke-width: 1;
        shape-rendering: crispEdges;
      }
      
      .extent {
        stroke: #fff;
        fill-opacity: .125;
        shape-rendering: crispEdges;
      }
    </style>
    
    <body>
    
      <!-- load the d3.js library -->
      <script src="http://d3js.org/d3.v3.min.js"></script>
    
      <script>
        // Set the dimensions of the canvas / graph
        var margin = {
            top: 30,
            right: 20,
            bottom: 30,
            left: 50
          },
          width = 600 - margin.left - margin.right,
          height = 270 - margin.top - margin.bottom;
    
        // Parse the date / time
        var parseDate = d3.time.format("%d-%b-%y").parse,
          formatDate = d3.time.format("%d-%b"),
          bisectDate = d3.bisector(function(d) {
            return d.date;
          }).left;
    
        // Set the ranges
        var x = d3.time.scale().range([0, width]);
        var y = d3.scale.linear().range([height, 0]);
    
        // Define the axes
        var xAxis = d3.svg.axis().scale(x)
          .orient("bottom").ticks(5);
    
        var yAxis = d3.svg.axis().scale(y)
          .orient("left").ticks(5);
    
        // Define the line
        var valueline = d3.svg.line()
          .x(function(d) {
            return x(d.date);
          })
          .y(function(d) {
            return y(d.close);
          });
          
        var area = d3.svg.area()
          .x(function(d) {
              return x(d.date);
            })
          .y0(height)
          .y1(function(d) {
            return y(d.close);
          });
    
        // Adds the svg canvas
        var svg = d3.select("body")
          .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 defs = svg.append("defs");
    
        var areaClip = defs.append("clipPath")
          .attr("id", "areaClip")
          .append("rect")
          .attr("x", width)
          .attr("y", 0)
          .attr("width", width)
          .attr("height", height);
    
        var lineSvg = svg.append("g");
    
        var focus = svg.append("g")
          .style("display", "none");
          
        var brush = d3.svg.brush()
          .x(x)
          .on("brush", function() {
            var s = brush.extent(),
              x1 = x(s[0]),
              x2 = x(s[1]);
              
            areaClip.attr('x', x1);
            areaClip.attr('width', x2 - x1);
          })
    
        var csv = `date,close
    26-Mar-12,606.98
    27-Mar-12,614.48
    28-Mar-12,617.62
    29-Mar-12,609.86
    30-Mar-12,599.55
    2-Apr-12,618.63
    3-Apr-12,629.32
    4-Apr-12,624.31
    5-Apr-12,633.68
    9-Apr-12,636.23
    10-Apr-12,628.44
    11-Apr-12,626.20
    12-Apr-12,622.77
    13-Apr-12,605.23
    16-Apr-12,580.13
    17-Apr-12,543.70
    18-Apr-12,443.34
    19-Apr-12,345.44
    20-Apr-12,234.98
    23-Apr-12,166.70
    24-Apr-12,130.28
    25-Apr-12,99.00
    26-Apr-12,89.70
    27-Apr-12,67.00
    30-Apr-12,53.98
    1-May-12,58.13`;
    
        var data = d3.csv.parse(csv);
    
        data.forEach(function(d) {
          d.date = parseDate(d.date);
          d.close = +d.close;
        });
    
        // 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 d.close+20;
        })]);
    
        // Add the valueline path.
        lineSvg.append("path")
          .attr("class", "line")
          .attr("d", valueline(data));
          
        lineSvg.append("path")
          .attr("d", area(data))
          .style("fill", "steelblue")
          .style("stroke", "none")
          .style("opacity", "0.5")
          .attr("clip-path", "url(#areaClip)")
    
        // Add the X Axis
        svg.append("g")
          .attr("class", "x axis")
          .attr("transform", "translate(0," + height + ")")
          .call(xAxis);
    
        // Add the Y Axis
        svg.append("g")
          .attr("class", "y axis")
          .call(yAxis);
    
        // append the x line
        focus.append("line")
          .attr("class", "x")
          .style("stroke", "blue")
          .style("stroke-dasharray", "3,3")
          .style("opacity", 0.5)
          .attr("y1", 0)
          .attr("y2", height);
    
        // append the y line
        focus.append("line")
          .attr("class", "y")
          .style("stroke", "blue")
          .style("stroke-dasharray", "3,3")
          .style("opacity", 0.5)
          .attr("x1", width)
          .attr("x2", width);
    
        // append the circle at the intersection
        focus.append("circle")
          .attr("class", "y")
          .style("fill", "none")
          .style("stroke", "blue")
          .attr("r", 4);
    
        // place the value at the intersection
        focus.append("text")
          .attr("class", "y1")
          .style("stroke", "white")
          .style("stroke-width", "3.5px")
          .style("opacity", 0.8)
          .attr("dx", 8)
          .attr("dy", "-.3em");
        focus.append("text")
          .attr("class", "y2")
          .attr("dx", 8)
          .attr("dy", "-.3em");
    
        // place the date at the intersection
        focus.append("text")
          .attr("class", "y3")
          .style("stroke", "white")
          .style("stroke-width", "3.5px")
          .style("opacity", 0.8)
          .attr("dx", 8)
          .attr("dy", "1em");
        focus.append("text")
          .attr("class", "y4")
          .attr("dx", 8)
          .attr("dy", "1em");
    
        // append the rectangle to capture mouse
        var context  = svg.append("g");
        
        context.call(brush);
        
        context.selectAll(".resize").append("path")
          .attr("d", "M0,2V" + (height - 2))
          .style("stroke", "black")
        
        context.select(".extent")
          .attr("height", height - 2)
          .attr("fill", "none");
        
        context.select(".background")
          .attr("height", height)
          .on("mouseover.tooltip", function() {
            focus.style("display", null);
          })
          .on("mouseout.tooltip", function() {
            focus.style("display", "none");
          })
          .on("mousemove.tooltip", 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.select("circle.y")
            .attr("transform",
              "translate(" + x(d.date) + "," +
              y(d.close) + ")");
    
          focus.select("text.y1")
            .attr("transform",
              "translate(" + x(d.date) + "," +
              y(d.close) + ")")
            .text(d.close);
    
          focus.select("text.y2")
            .attr("transform",
              "translate(" + x(d.date) + "," +
              y(d.close) + ")")
            .text(d.close);
    
          focus.select("text.y3")
            .attr("transform",
              "translate(" + x(d.date) + "," +
              y(d.close) + ")")
            .text(formatDate(d.date));
    
          focus.select("text.y4")
            .attr("transform",
              "translate(" + x(d.date) + "," +
              y(d.close) + ")")
            .text(formatDate(d.date));
    
          focus.select(".x")
            .attr("transform",
              "translate(" + x(d.date) + "," +
              y(d.close) + ")")
            .attr("y2", height - y(d.close));
    
          focus.select(".y")
            .attr("transform",
              "translate(" + width * -1 + "," +
              y(d.close) + ")")
            .attr("x2", width + width);
        }
        
      </script>
    </body>