yunzen
yunzen

Reputation: 33449

Ghost Markers in Leaflet on zoom and/or pan

I'm facing the display of ghost markers in the top left corner of my leaflet map (with marker-cluster) assembled in svelte.

The markers disappear, when they are 'unclustered' through zooming.

I need to get rid of the ghost markers

This is a screencast of the behavior: https://screencast-o-matic.com/watch/c0VQIhVwYJb
It's even clearer what happens, if I simulate offline after load: https://somup.com/c0VQIOxVwu

The screencast is taken from a REPL I made for demonstration: https://svelte.dev/repl/0f6f706f236b4b6581861a5496e6a743?version=3.55.0

The relevant (IMHO) code is this:

<!-- App.svelte -->
<script> // define map, initialView and markers </script>
<Leaflet bind:map view={initialView}>
    <MarkerCluster>
        {#each markers as {lat, lng}, index}
        <Marker latLng={[lat, lng]}>
            <Pin>{index}</Pin>
        </Marker>
        {/each}
    </MarkerCluster>
</Leaflet>
<!-- Leaflet.svelte -->
<script>
  function createLeaflet(node) {

    map = L.map(node, {
      zoomControl: true,
      gestureHandling: true,
    })    ;
        map.setView(view, zoom);
    
    const tileLayer = L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
      minZoom,
      maxZoom,
      useCache: true,
      crossOrigin: true,
    })
    tileLayer.addTo(map);
    
    return {
      destroy() {
        map.remove();
        map = undefined;
      },
    };
  }
</script>
<div style="height:{height};width:{width}" use:createLeaflet>
  {#if map}
    <slot {map} />
  {/if}
</div>
<!-- MarkerCluster.svelte -->
<script>
    const clusterGroup = L.markerClusterGroup({
        showCoverageOnHover: false,
        zoomToBoundsOnClick: true,
        spiderfyOnMaxZoom: true,
        spiderfyDistanceMultiplier: 2,
    });
    const layer = getContext("layer");
    setContext("layer", () => clusterGroup);

    $: layer().addLayer(clusterGroup);
</script>

<slot />
<!-- Marker.svelte -->
<script>
  function createMarker(
    markerElement,
    { width, height, anchorX, anchorY, latLng }
  ) {
    let icon = L.divIcon({
      html: markerElement,
      className: "map-marker",
      iconSize: L.point(width, height),
      iconAnchor: L.point(anchorX, anchorY),
    });
    marker = L.marker(latLng, { icon, title, alt, zIndexOffset, riseOnHover, riseOffset, opacity }).addTo(layer());
    marker.data = {...data}
    markers.push(marker)
    
    return {
      update({
        width,
        height,
        anchorX,
        anchorY,
        latLng,
      }) {
        let icon = L.divIcon({
          html: markerElement,
          className: "map-marker",
          iconSize: L.point(width, height),
          iconAnchor: L.point(anchorX, anchorY),
        });
        marker.setIcon(icon);
      },
      destroy() {
        if (marker) {
          marker.remove();
          marker = undefined;
        }
      },
    };
  }
</script>

<div use:createMarker>
    {#if marker}
    <slot />
    {/if}
</div>

Upvotes: 0

Views: 193

Answers (1)

hackape
hackape

Reputation: 19987

DOM struct

If you inspect the HTML elements from devtools. You'll see that when the markers are not ready to display, they're appended to the end of .leaflet-container element.

When they're ready, they got moved into .leaflet-marker-icon element, which you've customized to also have a classname .map-marker.

With this knowledge, the problem can be easily fixed with some CSS.

Marker.svelte

<div
    use:createMarker={{
      width,
      height,
      anchorX,
      anchorY,
      latLng,
    }}
    class={(classNames ?? '') + ' marker-html-element'}
    >
    {#if marker}
    <slot />
    {/if}
</div>


<style>
    :global(.map-marker .marker-html-element) {
        display: block;
    } 
    :global(.marker-html-element) {
        display: none;
    }
</style>

Upvotes: 0

Related Questions