Lucas Eckman
Lucas Eckman

Reputation: 13

d3-zoom breaks when cursor is over an inner svg element

I have implemented d3-zoom by following this brief tutorial.

I'm using https://d3js.org/d3.v5.min.js. This is my first project with d3.

My goal is to have a kind of floor plan showing booth tables at a venue. Similar to the tutorial, I've drawn shape elements from an array. In my case I've entered an array of booth information into a grid of elements.

The zoom functionality works just fine, except when my cursor is over the border or fill of one of my rectangles, or on the text of a element. If the point of my cursor is touching any of these elements, the zooming behavior stops working.

Try to zoom with the mousewheel with your cursor in blank space versus touching a shape or text.

I've tried to fit a console.log in somewhere to see what's not getting passed in the event, but have had trouble even finding where I can get the event argument.

Any help greatly appreciated! Here is my code:

var svg = d3.select("#venue-svg"); // this is my svg element

// the zoom rectangle. from the tutorial: 'The zoom behavior is applied 
// to an invisible rect overlaying the SVG element; this ensures that it 
// receives input, and that the pointer coordinates are not affected by 
// the zoom behavior’s transform.'

svg.append("rect")
  .attr("width", "100%")
  .attr("height", "100%")
  .style("fill", "none")
  .style("pointer-events", "all")
  .call(
    d3
      .zoom()
      .scaleExtent([1 / 2, 4])
      .on("zoom", zoomed)
  );

function zoomed() {
  g.attr("transform", d3.event.transform);
}

// a parent <g> that holds everything else and is targeted
// for the transform (from the tutorial). 
var g = svg.append("g");

// the groups that hold each booth table, associated org name, etc.
var tables = g
  .selectAll("g")
  .data(venueBooths)
  .enter()
  .append("g")
  .attr("transform", function(d) {
    return "translate(" + d.x + " " + d.y + ")";
  });

var tableRects = tables
  .append("rect")
  .attr("stroke", "steelblue")
  .attr("stroke-width", "2px")
  .attr("width", function(d) {
    return d.w;
  })
  .attr("height", function(d) {
    return d.h;
  })
  .attr("fill", "none")
  .attr("fill", function(d) {
    return $.isEmptyObject(d.reservation) ? "none" : "#FF5733";
  })
  .attr("id", function(d) {
    return "table-" + d.id;
  });

tables
  .append("text")
  .text(function(d) {
    return "Booth " + d.id;
  })
  .attr("dx", 5)
  .attr("dy", 60)
  .attr("font-size", "8px");

tables
  .append("text")
  .text(function(d) {
    return d.reservation.orgName ? d.reservation.orgName : "Available";
  })
  .attr("dy", 15)
  .attr("dx", 5)
  .attr("font-size", "9px")
  .attr("font-weight", "bold");

Upvotes: 1

Views: 243

Answers (1)

valangar
valangar

Reputation: 171

Try creating the rect in the end such that the DOM looks like this:

<svg>
    <g></g>
    <rect></rect>
</svg>

Since the zoom function is attached to the large rectangle, creating the smaller boxes above it prevents a zoom event from propagating to the large rectangle below them. It works for the boxes with a fill: none; since it behaves like a hollow box.

Try modifying the code to something like:

var svg = d3.select("#venue-svg"); // this is my svg element

// the zoom rectangle. from the tutorial: 'The zoom behavior is applied 
// to an invisible rect overlaying the SVG element; this ensures that it 
// receives input, and that the pointer coordinates are not affected by 
// the zoom behavior’s transform.'

function zoomed() {
  g.attr("transform", d3.event.transform);
}

// a parent <g> that holds everything else and is targeted
// for the transform (from the tutorial). 
var g = svg.append("g");

// the groups that hold each booth table, associated org name, etc.
var tables = g
  .selectAll("g")
  .data(venueBooths)
  .enter()
  .append("g")
  .attr("transform", function(d) {
    return "translate(" + d.x + " " + d.y + ")";
  });

var tableRects = tables
  .append("rect")
  .attr("stroke", "steelblue")
  .attr("stroke-width", "2px")
  .attr("width", function(d) {
    return d.w;
  })
  .attr("height", function(d) {
    return d.h;
  })
  .attr("fill", "none")
  .attr("fill", function(d) {
    return $.isEmptyObject(d.reservation) ? "none" : "#FF5733";
  })
  .attr("id", function(d) {
    return "table-" + d.id;
  });

tables
  .append("text")
  .text(function(d) {
    return "Booth " + d.id;
  })
  .attr("dx", 5)
  .attr("dy", 60)
  .attr("font-size", "8px");

tables
  .append("text")
  .text(function(d) {
    return d.reservation.orgName ? d.reservation.orgName : "Available";
  })
  .attr("dy", 15)
  .attr("dx", 5)
  .attr("font-size", "9px")
  .attr("font-weight", "bold");

svg.append("rect")
  .attr("width", "100%")
  .attr("height", "100%")
  .style("fill", "none")
  .style("pointer-events", "all")
  .call(
    d3
      .zoom()
      .scaleExtent([1 / 2, 4])
      .on("zoom", zoomed)
  );

Upvotes: 2

Related Questions