'Detecting Mouse Events on Multiple Overlapping SVG Elements

I'm trying to detect mousemove events on partially overlapping SVG elements, as in this image

enter image description here

fiddle

<svg>
    <rect id="red"    x=10 y=10 width=60 height=60 style="fill:#ff0000" />
    <rect id="orange" x=80 y=10 width=60 height=60 style="fill:#ffcc00" />
    <rect id="blue"   x=50 y=30 width=60 height=60 style="fill:#0000ff; fill-opacity: 0.8" />
</svg>

$('rect').on('mousemove', function()
{
    log(this.id);
});

Now, when hovering the mouse over the blue/red intersection I'd like to detect mouse events on both those elements, and the same for the blue/orange combo. As you can see in the logs, in those cases the event is currently only fired for the blue box as it is on top.

This has to do with pointer-events, as I can get the red and orange elements to fire the event while hovering the blue element by setting the blue element's pointer-events to none. But then I don't get the events for the blue box, so that is not a viable option either.

I will use whichever library solves this problem. I looked at event bubbling like in this d3 example, but that only works for elements that are nested in the DOM. I have lots of independent elements that may overlap with lots of other elements and can therefore not structure my DOM that way.

I'm guessing the last resort is to find the elements that are at the current mouse position, and manually firing the events. Therefore, I looked at document.elementFromPoint(), but that would only yield 1 element (and may not work in SVG?). I found the jQuerypp function within, that finds the elements at a given position, see here. That example looks great, except it's DIVs and not inside SVG. When replacing divs with svg rectangle elements, the fiddle seems to break.

What do I do?!



Solution 1:[1]

For anyone still looking, elementsFromPoint() returns a node list of all the elements under your mouse cursor.

NOTE: there is also a elementFromPoint() method.

This is particularly useful when you need to detect multiple overlapping SVG path elements on mouseover.

A simple example:

Get the nodeList from your mouse event.

const _overlapped = document.elementsFromPoint(e.pageX, e.pageY)

Filter the list based on some criterion:

// Some list of element id's you're interested in
const _lines = ['elId1', 'elId2', 'elId3'] 

// Check to see if any element id matches an id in _lines   
const _included = _overlapped.filter(el => _lines.includes(el.id))

// Perform an action on each member in the list
_included.forEach(...) 

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Vladimir Mujakovic