Leaflet JavaScript – Handling Mouseover Event with Two Markers in Leaflet

javascriptleaflet

I have a map on which one layer overlays the other one (I have icon markers displayed and larger circles markers behind them). I'm trying to create a function that changes the icon symbol AND the circle symbology while hovering over a feature. I need both icon and circle markers to appear at the same time, when the mouse gets over the icon.

What's happening so far is that I can't display the updated icon and circle together, I have to slightly hover on the sides to display the icon (as in the images, red cross is my cursor). What is my mistake ?

Circle updated
Icon updated

Here is my code :

//Initial circle symbology - circles are invisible
var buffer_zone = L.geoJSON(studios, {
    onEachFeature: mouse_events_buffer,
    pointToLayer: function (zone, coord){
    var circlebuff = new L.circle(coord, 100, {color : '#006080', fillColor : '#99ccff', fillOpacity : 0, opacity : 0, weight : 1});
    return circlebuff;
    }
}).addTo(map);

//Initial icon style
var studioicon = L.icon({
    iconUrl: 'cinema.png',
    iconSize: [40, 40],
    iconAnchor: [15,15]
  });
      
//Icon marker and icon style that should appear when hovering
var studios_marker = L.geoJSON(studios, {
onEachFeature: mouse_events_studios,
pointToLayer: function (feature, latlng){
    var smallIcon = L.icon({
    iconUrl: ''+ feature.properties.Name +'.png',
    iconSize: [80, 60],
    iconAnchor: [15,15]
    });
    var marker = L.marker(latlng, {icon: studioicon});
    marker.smallIcon = smallIcon;
    return marker;
}
}).addTo(map);


function mouse_events_buffer(feature, buff){

//Change circle style (ie add color and bigger radius)
    buff.on('mouseover', function(buffer_on){
        var obj_buffer1 = buffer_on.target;
        buff.setStyle({color : '#006080', fillColor : '#99ccff', 
        fillOpacity : 0.3, opacity : 1, weight : 1});
        buff.setRadius(3000)
    });

//Set the original style back
    buff.on('mouseout', function(buffer_off){
        var obj_buffer2 = buffer_off.target;
        buffer_zone.resetStyle(buffer_off.target);
    });
}
  
function mouse_events_studios(feature, marker){

//Change icon style (different png and size)
    marker.on('mouseover', function(alter_style){
        var leaflet_obj2 = alter_style.target;
        marker.setIcon(marker.smallIcon);
    });

//Set the original style back
    marker.on('mouseout', function(origin_style){
        var leaflet_obj3 = origin_style.target;
        marker.setIcon(studioicon);
    });

//Additionnal action (this works)
    marker.on('mouseover', function(resume){
        var leaflet_obj4 = resume.target;
        var info1 = document.getElementById("studio_name");
        info1.innerHTML = leaflet_obj4.feature.properties.Name
        var info2 = document.getElementById("studio_resume");
        info2.innerHTML = leaflet_obj4.feature.properties.resume
    })
}

Best Answer

Event mouseout gets fired when mouse leaves feature/element or when it enters another feature/HTML element, even when the second feature/element is inside area of the first feature/element. That's the reason why buffer zone circle feature gets hidden when mouse enters studio marker, which lies inside buffer zone circle.

HTML DOM model has also mouseleave event, which gets fired only when mouse goes outside area of an HTML element, but unluckily Leaflet does not treat this event separately from mouseout event.

One possible solution in your case would be to check distance from circle center when mouseout event is fired. Circle can then be hidden only when distance is bigger that circle radius.

There is also no need to have two GeoJSON layers, one for markers and other for buffers. It all can be done with a single layer and only pointToLayer option function.

Code could then look something like this:

var radiusTransparent = 100;
var radiusVisible = 3000;

var studioicon = L.icon({
  iconUrl: 'cinema.png',
  iconSize: [40, 40],
  iconAnchor: [15,15]
});
  
function setBufferVisible(buffer, setVisible) {
  if (setVisible) {
    buffer.setStyle(visibleBufferStyle);
    buffer.setRadius(radiusVisible);
    }
  else {
    buffer.setStyle(transparentBufferStyle);
    buffer.setRadius(radiusTransparent);
  }
}

function showStudioInfo(name, resume) {
  var info1 = document.getElementById("studio_name");
  info1.innerHTML = name;
  var info2 = document.getElementById("studio_resume");
  info2.innerHTML = resume;
}

var visibleBuffer = null;
var studios_marker = L.geoJSON(studios, {
  pointToLayer: function (feature, latlng){
    var smallIcon = L.icon({
      iconUrl: ''+ feature.properties.Name +'.png',
      iconSize: [80, 60],
      iconAnchor: [15,15]
    });
    var marker = L.marker(latlng, {icon: studioicon});
    marker.smallIcon = smallIcon;
    marker.feature = feature;
    marker.on('mouseover', function(evt) {
      marker.setIcon(marker.smallIcon);
      var properties = marker.feature.properties;
      showStudioInfo(properties.Name, properties.resume);
    });
    marker.on('mouseout', function(evt){
      marker.setIcon(studioicon);
      showStudioInfo('', '');
    });

    var buffer = new L.circle(latlng, radiusTransparent, transparentBufferStyle);
    buffer.on('mouseover', function(evt){
      if (visibleBuffer) {
        setBufferVisible(visibleBuffer, false);
      }
      setBufferVisible(buffer, true);
      visibleBuffer = buffer;
    });
    buffer.on('mouseout', function(evt){
      var distance = map.distance(evt.latlng, latlng);
      if (distance > radiusVisible) {
        setBufferVisible(buffer, false);
      }
    });
    
    var markerGroup = L.layerGroup([buffer, marker]);
    return markerGroup;
  }
}).addTo(map);