Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 - Appending Rectangle Stops Tooltip From Working

I plotted a data set with tooltips enabled. However, if I add further elements to the svg, the tooltip part generates an error. As a concrete example, I have:

var canvas = ....;
canvas.selectAll("rect")
.data(data)
...
...
.append("title")
.text(function(d){return tooltiptext;});
...
canvas.append("rect").attr("x",0).attr("y",0)
.attr("width",w).attr("height",h).style("fill","purple")
.style("fill-opacity",0.1);

Adding the last canvas.append(...) makes the tooltip part not work. How do I fix this?

Here is a working jsfiddle https://jsfiddle.net/kbkbkb/rkok5zmr/

Thank you

like image 897
kbkbkb Avatar asked Jul 15 '26 10:07

kbkbkb


1 Answers

The last rectangle you append sits on top of everything. This effectively blocks the mouseover events of the elements underneath.

In svg, elements are layered in the order they are appended (newly appended are on top of previously appended), and pointer/mouse events don't see through other elements by default.

To fix this, you can set the pointer-events property of the purple rectangle to none:

.style("pointer-events","none")

var data = [5, 6, 3, 7, 10];

var w = 300,
  h = 200;

var ys = d3.scale.linear().domain([0, 10]).range([0, 0.9 * h]);
var bw = Math.floor(w / data.length);

var svg = d3.select("body")
  .append("svg")
  .attr("width", w)
  .attr("height", h);

svg.selectAll("rect")
  .data(data)
  .enter()
  .append("rect")
  .attr("width", bw * 0.9)
  .attr("height", function(d) {
    return ys(d);
  })
  .attr("x", function(d, i) {
    return bw * i;
  })
  .attr("y", function(d) {
    return h - ys(d);
  })
  .append("title")
  .text(function(d) {
    return d;
  });


svg.append("rect")
  .attr("x",0)
    .attr("y",0)
    .attr("width",w)
    .attr("height",h)
    .style("fill","purple")
    .style("fill-opacity",0.1)
    .style("pointer-events","none");  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

This effectively makes the element see through for mouse events.

Alternatively you could set a class or id for the purple rectangle and use css to set pointer-events to none for it:

var data = [5, 6, 3, 7, 10];

var w = 300,
  h = 200;

var ys = d3.scale.linear().domain([0, 10]).range([0, 0.9 * h]);
var bw = Math.floor(w / data.length);

var svg = d3.select("body")
  .append("svg")
  .attr("width", w)
  .attr("height", h);

svg.selectAll("rect")
  .data(data)
  .enter()
  .append("rect")
  .attr("width", bw * 0.9)
  .attr("height", function(d) {
    return ys(d);
  })
  .attr("x", function(d, i) {
    return bw * i;
  })
  .attr("y", function(d) {
    return h - ys(d);
  })
  .append("title")
  .text(function(d) {
    return d;
  });


svg.append("rect")
  .attr("x",0)
    .attr("y",0)
    .attr("width",w)
    .attr("height",h)
    .style("fill","purple")
    .style("fill-opacity",0.1)
    .attr("class","overlay")
.overlay{
  pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Lastly, as a different approach, you could append the purple rectangle first, so that the bars of the chart are ontop of it, and there is no need to change the pointer-events setting. Note that this method requires a change to your enter selection for the bar chart - selectAll("rect") will include the purple rectangle, so you could change this to selectAll(null), as I did in my example.

var data = [5, 6, 3, 7, 10];

var w = 300,
  h = 200;

var ys = d3.scale.linear().domain([0, 10]).range([0, 0.9 * h]);
var bw = Math.floor(w / data.length);

var svg = d3.select("body")
  .append("svg")
  .attr("width", w)
  .attr("height", h);


svg.append("rect")
  .attr("x",0)
    .attr("y",0)
    .attr("width",w)
    .attr("height",h)
    .style("fill","purple")
    .style("fill-opacity",0.1)

svg.selectAll(null)
  .data(data)
  .enter()
  .append("rect")
  .attr("width", bw * 0.9)
  .attr("height", function(d) {
    return ys(d);
  })
  .attr("x", function(d, i) {
    return bw * i;
  })
  .attr("y", function(d) {
    return h - ys(d);
  })
  .append("title")
  .text(function(d) {
    return d;
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

I've changed your variable name canvas to svg in my answer as canvas can be misleading, since d3 visualizations often use canvas as opposed to svg and both involve different methods of implementation

like image 63
Andrew Reid Avatar answered Jul 18 '26 00:07

Andrew Reid