score:1

Accepted answer

Here's a working JS Fiddle: ScatterPlot with zoom and tooltips

Major changes:

  1. As you're appending the brush rect on top of the circles, the mouse events on the circles would never work. The order of the elements matters big time here, you see! So, here's what I did (moved brush appending code ABOVE the circles:

    svg.append("g")
     .attr("class", "brush")
     .call(brush);
    
    svg.selectAll("circle")
    ...
    
  2. I'm not sure why you were using mousemove when you want the tooltip only when you have the mouse over the circles. I changed the mouse events to this:

    .on("mouseover", function(d){
       tooltip.html(d[3]).style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px").style("visibility", "visible"); 
    }).on("mouseout", function(){return tooltip.style("visibility", "hidden");});
    
  3. You had the div element (used as a tooltip) within the SVG. I'm sure that might've been done in a hurry. Here: I moved it outside of SVG:

    var tooltip = d3.select(".two")
      .append("div")
    
  4. It's not recommended to use static numbers while setting the axes domains. You have the data and you know the x and y points to be fetched from, so just use d3.extent

    x0 = d3.extent(points, function (d) { return d[0]; }),
    y0 = d3.extent(points, function (d) { return d[1]; })
    

Minor changes:

  1. Using domain().nice() while setting the domains. It's really useful when you have negative values. Here's the doc
  2. Instead of using just 2 of your provided points, I generated a few random points and used it as the data: this clears up the zoom issue as well (in which I changed nothing as you had it working perfect)

    function generateRandomPoints () {
     var points = [];
     for(var i=0; i<10; i++) {
       points.push([Math.random()*30 - 15, Math.random()*20-5, i, 'Cluster '+ (i+1)]);
     }
     return points;
    }
    

Here's a snippet putting together all of the above:

  var data = [
  {
    "points": generateRandomPoints()
  }
];
  
  
  function generateRandomPoints () {
  	var points = [];
    for(var i=0; i<10; i++) {
    	points.push([Math.random()*30 - 15, Math.random()*20-5, i, 'Cluster '+ (i+1)]);
    }
    return points;
  }
  
  var points = data[0].points;

  var svg = d3.select("#cluster"),
      width = +svg.attr("width"),
      height = +svg.attr("height");

  var k = height / width,
      x0 = d3.extent(points, function (d) { return d[0]; }),
      y0 = d3.extent(points, function (d) { return d[1]; }),
      x = d3.scaleLinear().domain(x0).nice().range([0, width]),
      y = d3.scaleLinear().domain(y0).nice().range([height, 0]),
      z = d3.scaleOrdinal(d3.schemeCategory10);
  var xAxis = d3.axisTop(x).ticks(12),
      yAxis = d3.axisRight(y).ticks(12 * height / width);

  var brush = d3.brush().on("end", brushended),
      idleTimeout,
      idleDelay = 350;

  var tooltip = d3.select(".two")
      .append("div")
      .style("position", "absolute")
      .style("z-index", "10")
      .style("visibility", "hidden")
      .style("background", "#ffffff")
      .text("a simple tooltip");

  svg.append("g")
      .attr("class", "brush")
      .call(brush);
      
  svg.selectAll("circle")
    .data(points)
    .enter()
    .append("circle")
      .attr("cx", function(d) { 
      return x(d[0]); 
      })
      .attr("cy", function(d) { 
      return y(d[1]); 
      })
      .attr("r", 4)
      .attr("fill", function(d) { return z(d[2]); })
      .on("mouseover", function(d){
      	tooltip.html(d[3]).style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px").style("visibility", "visible"); })
      .on("mouseout", function(){return tooltip.style("visibility", "hidden");});

  svg.selectAll(".domain")
      .style("display", "none");

  function brushended() {
    var s = d3.event.selection;
    if (!s) {
      if (!idleTimeout) return idleTimeout = setTimeout(idled, idleDelay);
      x.domain(x0).nice();
      y.domain(y0).nice();
    } else {
      x.domain([s[0][0], s[1][0]].map(x.invert, x));
      y.domain([s[1][1], s[0][1]].map(y.invert, y));
      svg.select(".brush").call(brush.move, null);
    }
    zoom();
  }

  function idled() {
    idleTimeout = null;
  }

  function zoom() {
    var t = svg.transition().duration(750);
    svg.select(".axis--x").transition(t).call(xAxis);
    svg.select(".axis--y").transition(t).call(yAxis);
    svg.selectAll("circle").transition(t)
        .attr("cx", function(d) { return x(d[0]); })
        .attr("cy", function(d) { return y(d[1]); });
  }
<script src="https://d3js.org/d3.v4.min.js"></script>



<div class="one">
  <div class="two">
    <svg id="cluster" width="700" height="500"></svg>
  </div>
</div>

Hope this helps.


Related Query