mousemove with two layers on top of each other (overlapping)

Viewed 135

The closest to my question are: Mapbox gl js - overlapping layers and mouse event handling, Mapbox add popup on hover (layer), close on mouseleave, but keep open on popup hover and How to ignore mouse events on a mapbox layer - but they don't answer it.

I have two layers, let's imagine one is for a country, and another is for a city. Both of them a handled like this:

// CountryLayer.js
map.on("mousemove", "country-layer", e => {
   // show info about the country 
   featureProps = e.features[0] // display some notification from the props
   ...

// CityLayer.js
map.on("mousemove", "city-layer", e => {
   // show info about the city
   featureProps = e.features[0] // display some notification from the props
   ...

It's done in different components. But when I mouseover city-layer mapbox thinks that I'm still "mousemoving" on top of the country-layer as well, so I get two notifications from separate components, where I need only one - in that case from the city-layer cause it's on top of country-layer.

Handling the mousemove without layerId in one place is gonna be a mess and breaks all the good rules about programming. Creating external "event manager" which will track whether I'm hovering the city and if is so will remove mousemove event from country-layer - is complex. I didn't find any good alternatives. At least, I would be glad to disable pointer events for a layer like this:

map.on("mousemove", "city-layer", e => {
   map.getLayer("country-layer").setStyle({ "pointer-events": false })
   featureProps = e.features[0]
   ...

or something like this. Is it possible? Is there more adequate way around it?

e.originalEvent.stopPropagation(); does not work

2 Answers

It appears, that you can do e.originalEvent.preventDefault(); in the city-layer and add a check e.originalEvent.defaultPrevented in the country-layer from the example.

However, I have issues with z-index position of layers: map.moveLayer("city-layer", "country-layer"); or vice-vise doesn't actually change the way, event propogates, so for some reason my country-layer always come first, so when it checks for e.originalEvent.defaultPrevented, preventDefault() wasn't fired yet, so it comes always false.

But technically, this answers my question.

map.on can listen on multiple layers at once. With events like mousemove it returns features in reverse layer order (from "highest" to "lowest").

So the way to do this is:

map.on("mousemove", ["city-layer", "country-layer"], e => {
   feature = e.features[0]
   if (feature.layer.id === 'city-layer') {
     // ...
   } else {
     // ...
   }
});
Related