Search code examples
javascriptjqueryleaflet

how to keep class attribute stored for Leaflet.js markers


I have a set of markers each stored in different Layers... lets say:

let earth= {
    layer:{
        ...
        Markers:{...}
    }
}
let mars= {
    layer:{
        ...
        Markers:{...}
    }
}
let pluto= {
    layer:{
        ...
        Markers:{...}
    }
}

I switch to different maps (with individual markers) with this function:

function setMap(selectedMap, htmlElement, ifSub = false) {
    if (currentMap != selectedMap) {
        map.removeLayer(window[currentMap].Layer)
        map.addLayer(window[selectedMap].Layer)

        currentMap = selectedMap
    }
}

With a button (and on launch) I have given some of the markers on pluto a class (lets say "Visited") which changes css for them.

However when I change the map to Earth and back to Pluto the markers from Pluto "forget" that they had a class in the first place. how would I keep the css stored with the marker? And added back to the marker when map.addLayer(...) is called?


Solution

  • The problem here is that when a Marker is removed from the map, its icon image is deleted, so any classes or CSS styling you had added manually to it are lost. A new image is created the next time the Marker is added to the map again.

    If you just need to make simple changes to the appearance of marker icons, you can create a set of icons with the different images and classes you need, and set the Marker to use the appropriate one. You can set the appropriate initial icon using myMarker = new L.Marker(latlngs, {icon: myIcon}), then change it later with mymarker.setIcon(myOtherIcon). The Marker will remember which Icon it has been set to use, even when it has been removed from the map and added back again. The Leaflet documentation includes a tutorial on using custom icons that shows how to create your own icons. If you want them to have different classes, use the className option when creating them.

    Simply using icons with different classes may not work if you need to be able to add and remove classes independently. However, we can extend the definition of a Marker to add class-setting functionality. The following code creates a new class of L.ClassedMarker objects, based on the source code for L.Marker:

    L.ClassedMarker = L.Marker.extend({
      options: {
        classes: []
      },
      addClass: function(className) {
        if(this.options.classes.indexOf(className) === -1) {
          this.options.classes.push(className);
        }
        this._initIcon();
      },
      getClasses: function() {
        return this.options.classes;
      },
      removeClass: function(className) {
        var index = this.options.classes.indexOf(className);
        if(index > -1) {
          this.options.classes.splice(index, 1);
        }
        this._initIcon();
      },
      _initIcon: function() {
        L.Marker.prototype._initIcon.apply(this, null);
        for(let cls of this.options.classes) {
          L.DomUtil.addClass(this._icon, cls);
        }
      }
    });
    L.classedMarker = function(latLng, options) {
      return new L.ClassedMarker(latLng, options);
    }
    

    We can use a ClassedMarker in the same way as a normal Marker, but when creating it, we can specify an (optional) list of classes to add to the icon:

    var myMarker = L.classedMarker(latlngs, {classes: ["new"]}).addTo(map);
    

    You can also add or remove classes after the ClassedMarker has been created:

    myMarker.removeClass("new");
    myMarker.addClass("visited");
    

    The ClassedMarker will remember which classes you have added, regardless of whether you add or remove it from the map, or change the marker's icon. You can check what classes have been added using myMarker.getClasses(). Note this only returns your custom classes, not the ones used internally by Leaflet, or set using the className option of the marker's icon.

    The code works by maintaining a list of classes in the ClassedMarker's options, rather than in the icon. Each time the icon needs to be updated, it calls the _initIcon() code from the original L.Marker class, but then uses L.DomUtil.addClass() to apply the stored class names to the new icon image.