d3.on("mouseover") event does not work with nested SVG elements
Asked Answered
B

5

9

I have a nested set of elements (SVG). The root element is the graph, and the children are elements in the graph (lines, axis, etc.). Simplified example:

<g transform="translate(80,10)" id="mainGraph">
    <g class="Line">
        <path d="....."></path>
    </g>
</g>

My problem is that if I bind a mouseover/mousemove event (with D3.on("mouseover") for example) to the mainGraph element, it only triggers if I move the mouse over one of the child elements.

One of the things I read is that there is priority of later elements, so I added .style("pointer-events","none") to all child elements, but that didn't work.

Bicollateral answered 5/7, 2013 at 18:28 Comment(0)
H
15

One approach is to add a rectangle filling the whole surface as the first element to catch mouse events that are not caught by elements added later:

something.append('svg:rect')
  .attr('width', width) // the whole width of g/svg
  .attr('height', height) // the whole heigh of g/svg
  .attr('fill', 'none')
  .attr('pointer-events', 'all')
  .on('mouseover', function() {
      // do something
    }     
  });

I believe the <g> element (mainGraph in your example) is just a container, not an actual shape that can receive mouse events.

You might be able to add the mouse event listener on the svg element itself, but I don't think the <g> will work.

Heartsome answered 5/7, 2013 at 19:6 Comment(3)
Using the g should work if you set its dimensions explicitly.Silvana
A g element (unlike a div) is just a shapeless container, and so has no dimensions. So, if you want the mouseover to apply to a rectangular area, this you’ll need to specify rectangular content such as this rect. However, one useful refinement is using fill: none and pointer-events: all styles.Decent
@Decent does it make any difference if using pointer-events directly on the element vs setting it in its CSS properties? Does a path element inside the g behave the same way?Discoloration
B
6

Apart from using a rect element, it should have a CSS property set like this pointer-events: all; for the events to be triggered.

Bendy answered 14/5, 2016 at 1:0 Comment(1)
Yes, to set the pointer-events is the key, no matter if it's nested or not. The default value will make it only accept events when visiblePainted, see developer.mozilla.org/en-US/docs/Web/CSS/pointer-events.Liatris
T
1

I also face the same problems.
The solution is to add the css property to parent element

pointer-events: stroke;

or

pointer-events: visibleStroke;
Twophase answered 8/12, 2017 at 11:13 Comment(0)
B
0

Had a very similar issue. Was being caused by me declaring the event listener twice EG:

.on('mousemove', function (event, d) {
// Mouse event stuffs 
}
.on('mousemove', function (event, d) {
// Mouse event stuffs again (overwrites above declaration).
}

Fixed by only declaring one listener function per event instead of two.

D3 will not warn you about being this stupid. So make sure you only are declaring each mouse related event listener once.

Boland answered 31/3, 2021 at 0:56 Comment(0)
A
0

I also faced the same problems. I had the following code

svg.append("rect")
        .attr("fill",  "none")
        .attr("width", 20)
        .attr("height", 20)
        .on("mouseover", (event, data)=> { console.log(data)})
        .on("mouseout", (event, data)=> { console.log(data)});

where fill attribute is set to none. Once I removed fill attribute or set it to white, events started working as expected. So my new code looks like

svg.append("rect")
        .attr("fill",  "white")
        .attr("width", 20)
        .attr("height", 20)
        .on("mouseover", (event, data)=> { console.log(data)})
        .on("mouseout", (event, data)=> { console.log(data)})
Augusto answered 30/4, 2022 at 10:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.