Search code examples
javascriptreactjstypescriptazureazure-maps

Why doesn't my mouseover event on a react-azure-maps SymbolLayer ever get fired?


I am using the react-azure-maps library to use Azure Maps from a React app. I am trying to add a mouseover event on my markers. However, the event handler is never even called.

I tried the following code:

const { mapRef, isMapReady } = useContext<IAzureMapsContextProps>(AzureMapsContext);

const layerMarker = new layer.SymbolLayer(dataSourceMarker);
mapRef.layers.add(layerMarker);

mapRef.events.add("mouseover", layerMarker, (e) => {
    console.log('Event handler worked', e)
})

The Azure Maps example for this case features code that's very similar to the following:

var symbolLayer = new atlas.layer.SymbolLayer(datasource, null, {
                    iconOptions: {
                        allowOverlap: true,
                        ignorePlacement: true
                    }
                });
map.layers.add(symbolLayer);
map.events.add('mouseover', symbolLayer, highlight);

// ...
function highlight(e) {
        //Highlight the div to indicate that the event has fired.
        document.getElementById(e.type).style.background = 'LightGreen';

        //Remove the highlighting after a second.
        setTimeout(function () { document.getElementById(e.type).style.background = 'white'; }, 1000);
    }

This is quite similar to what I tried, and I can see from their example that it works fine for them. Unfortunately, it doesn't work for me at all, even though when I log the layer objects I can see my event handlers in the lists of listeners.

I found this GitHub issue (which suggests using the click handler without passing the layers). However, this really isn't what I want because the MouseOver event apparently only triggers once (when I drag the cursor off the map and back, and not even specifically when the user is hovering over one of the markers). I would like my events to trigger when, and only when, the user is hovering over one of the markers.

I saw this Q&A, but that doesn't help me because my event handlers are never even triggered in the first place.

I also saw this Q&A, which helped with part of my original problem but didn't solve the fact that the event handlers aren't triggered.

Oddly enough, Visual Studio Code also gives me the following error message (which may or may not be related to my problem):

No overload matches this call.
  The last overload gave the following error.
    Argument of type '"mouseover"' is not assignable to parameter of type '"sourceadded" | "sourceremoved"'.ts(2769)

I see that there is, in fact, an overload that does what I want:

/**
         * Adds a mouse event to the Layer(s).
         * @param eventType The event name.
         * @param target The Layer(s) to add the event for.
         * @param callback The event handler callback.
         */
        add(eventType: "mousedown" | "mouseup" | "mouseover" | "mousemove" | "click" | "dblclick" | "mouseout" | "mouseenter" | "mouseleave" | "contextmenu", target: atlas.layer.Layer | atlas.layer.Layer[], callback: (e: MapMouseEvent) => void): void;

However, it appears to be "picking" the wrong overload (at least in Visual Studio Code). I don't know if this has anything to do with my problem or not, and I don't know exactly how to correct this.

Can someone explain what I'm doing wrong here?


Solution

  • Generally, I recommend against using the mouseover event as it doesn't work on the map WebGL canvas in the same way you would normally expect it to work on a DOM element. In the map, as soon as you mouseover any item in the layer, the event should fire, however if another symbol is close, you could move the mouse over the other symbol and the event won't first since the event is layer level, not symbol level. Instead, I recommend using the mousemove event and tracking the ID of the currently hovered symbol. It may be easier to add this event to the map, that way you can also know when the mouse is no longer over a symbol.

    Here is an example that simply uses mousemove on the layer instead of mouseover.

    const { mapRef, isMapReady } = useContext<IAzureMapsContextProps>(AzureMapsContext);
    
    const layerMarker = new layer.SymbolLayer(dataSourceMarker);
    mapRef.layers.add(layerMarker);
    
    var currentShape = null;
    
    mapRef.events.add("mousemove", layerMarker, (e) => {
        console.log('Event handler worked', e)
        
        var shape = e.shapes[0];
        
        //Only react to it the first time it is mouse overed.
        if(shape != currentShape){
            //Keep track currently mouseovered shape.
            currentShape = shape;
    
            //New shape mouse overed.
        }
    })
    

    Here is a second example that adds the event to the map. The big benefit here is you could use a single event to manage data from multiple data sources which would provide a performance benefit. Note that this is data source level, and not layer level.

    const { mapRef, isMapReady } = useContext<IAzureMapsContextProps>(AzureMapsContext);
    
    const layerMarker = new layer.SymbolLayer(dataSourceMarker);
    mapRef.layers.add(layerMarker);
    
    var currentShape = null;
    
    mapRef.events.add("mousemove", (e) => {
        console.log('Event handler worked', e)  
        
        var shape = e.shapes[0];
        
        //Try and get the shape from the data source to verify if it is contained in it.
        if(shape instanceof atlas.Shape && dataSourceMarker.getShapeById(shape.getId()){
            //Only react to it the first time it is mouse overed.
            if(shape != currentShape){
                //Keep track currently mouseovered shape.
                currentShape = shape;
                
                //New shape mouse overed.
            }
        } else {
            //Event occured outside of the shapes we care about. If you are hiding/showing something based on the shape being hovered, hide it now.
        }
    })