OAN
OAN

Reputation: 198

Leaflet Trigger Event on Clustered Marker by external element

I just starting to learn about Leaflet.js for my upcoming project.

What i am trying to accomplish: I need to make a list of marker which displayed on the map, and when the list item is being hovered (or mouseover) it will show where the position on the map (for single marker, it should change its color. For Clustered marker, it should display Coverage Line like how it behave when we hover it.. and perhaps change its color too if possible). The map should not be changed as well as the zoom level, to put it simply, i need to highlight the marker/ Cluster on the map.

What i have accomplished now : I am able to do it on Single Marker. enter image description here

what i super frustrated about : I failed to find a way to make it happen on Clustered Marker.

I use global var object to store any created marker.

function updateMapMarkerResult(data) {
  markers.clearLayers();
  for (var i = 0; i < data.length; i++) {
    var a = data[i];
    var myIcon = L.divIcon({
      className: 'prop-div-icon',
      html: a.Description
    });
    var marker = L.marker(new L.LatLng(a.Latitude, a.Longitude), {
      icon: myIcon
    }, {
      title: a.Name
    });
    marker.bindPopup('<div><div class="row"><h5>Name : ' + a.Name + '</h5></div><div class="row">Lat : ' + a.Latitude + '</div><div class="row">Lng : ' + a.Longitude + '</div>' + '</div>');
    marker.on('mouseover', function(e) {
      if (this._icon != null) {
        this._icon.classList.remove("prop-div-icon");
        this._icon.classList.add("prop-div-icon-shadow");
      }
    });
    marker.on('mouseout', function(e) {
      if (this._icon != null) {
        this._icon.classList.remove("prop-div-icon-shadow");
        this._icon.classList.add("prop-div-icon");
      }
    });
    markersRef[a.LocId] = marker;   // <-- Store Reference
    markers.addLayer(marker);

    updateMapListResult(a, i + 1);
  }
  map.addLayer(markers);
}

But i don't know which object or property to get the Clustered Marker reference. And i trigger the marker event by my global variable (which only works on single marker).

...
li.addEventListener("mouseover", function(e) {
    jQuery(this).addClass("btn-info");
    markersRef[this.getAttribute('marker')].fire('mouseover'); // --> Trigger Marker Event "mouseover"
    // TODO : Trigger ClusteredMarker Event "mouseover"
  });
...

This is my current https://jsfiddle.net/oryza_anggara/2gze75L6/, any lead could be a very big help. Thank you.

Note: the only js lib i'm familiar is JQuery, i have no knowledge for others such as Angular.js

Upvotes: 2

Views: 3436

Answers (1)

ghybs
ghybs

Reputation: 53185

You are probably looking for markers.getVisibleParent(marker) method, to retrieve the containing cluster in case your marker is clustered.

Unfortunately, it is then not enough to fire your event on that cluster. The coverage display functionality is set on the Cluster Group, not on its individual clusters. Therefore you need to fire your event on that group:

function _fireEventOnMarkerOrVisibleParentCluster(marker, eventName) {
  var visibleLayer = markers.getVisibleParent(marker);

  if (visibleLayer instanceof L.MarkerCluster) {
    // In case the marker is hidden in a cluster, have the clusterGroup
    // show the regular coverage polygon.
    markers.fire(eventName, {
      layer: visibleLayer
    });
  } else {
    marker.fire(eventName);
  }
}

var marker = markersRef[this.getAttribute('marker')];
_fireEventOnMarkerOrVisibleParentCluster(marker, 'mouseover');

Updated JSFiddle: https://jsfiddle.net/2gze75L6/5/

That being said, I think another interesting UI, instead of showing the regular coverage polygon that you get when "manually" hovering a cluster, would be to spiderfy the cluster and highlight your marker. Not very easy to implement, but the result seems nice to me. Here is a quick try, it would probably need more work to make it bullet proof:

Demo: https://jsfiddle.net/2gze75L6/6/

function _fireEventOnMarkerOrVisibleParentCluster(marker, eventName) {
  if (eventName === 'mouseover') {
    var visibleLayer = markers.getVisibleParent(marker);

    if (visibleLayer instanceof L.MarkerCluster) {
      // We want to show a marker that is currently hidden in a cluster.
      // Make sure it will get highlighted once revealed.
      markers.once('spiderfied', function() {
        marker.fire(eventName);
      });
      // Now spiderfy its containing cluster to reveal it.
      // This will automatically unspiderfy other clusters.
      visibleLayer.spiderfy();
    } else {
      // The marker is already visible, unspiderfy other clusters if
      // they do not contain the marker.
      _unspiderfyPreviousClusterIfNotParentOf(marker);
      marker.fire(eventName);
    }
  } else {
    // For mouseout, marker should be unclustered already, unless
    // the next mouseover happened before?
    marker.fire(eventName);
  }
}

function _unspiderfyPreviousClusterIfNotParentOf(marker) {
  // Check if there is a currently spiderfied cluster.
  // If so and it does not contain the marker, unspiderfy it.
  var spiderfiedCluster = markers._spiderfied;

  if (
    spiderfiedCluster
    && !_clusterContainsMarker(spiderfiedCluster, marker)
  ) {
    spiderfiedCluster.unspiderfy();
  }
}

function _clusterContainsMarker(cluster, marker) {
  var currentLayer = marker;

  while (currentLayer && currentLayer !== cluster) {
    currentLayer = currentLayer.__parent;
  }

  // Say if we found a cluster or nothing.
  return !!currentLayer;
}

Upvotes: 5

Related Questions