34
votes

So i try to achieve a result as on foursquare: https://foursquare.com/explore?cat=drinks&mode=url&near=Paris which is when you clik on a marker on the map, it scrolls through the listed of restaurants on the right -hand side of the screen to the ad hoc restaurant, and highlights it through CSS. Conversely, when you click on the restaurant on the list, it highlights it on the map.

I am using skobbler/leaflet. I think I can achieve this by amending dynamically CSS as shown in this example: http://jsfiddle.net/gU4sw/7/ + a scroll to destination script already in place in the page.

To achieve this however, it looks like I need to assign an ID within the markers (2 markers below):

var marker = L.marker([52.52112, 13.40554]).addTo(map);
marker.bindPopup("Hello world!<br>I am a popup1.", { offset: new L.Point(-1, -41) }).openPopup();

var marker = L.marker([52.53552, 13.41994]).addTo(map);
marker.bindPopup("Hello world!<br>I am a popup2.", { offset: new L.Point(-1, -41) }).openPopup();

Question is: How can I assign an marker ID to trigger css change in the corresponding element within my html page?

My knowledge of JS is very limited, but may be there's a nice and easy solution out there, thx

7
Know I'm very late here but you would have to give each marker an ID as mentioned in @markoletic's answer. Also in your code your second marker overrides the first since you're saving them to the same var marker. I added a more detailed answer below.Greg Venech

7 Answers

51
votes

I've been looking for a nice way to do this and as far as I can tell there is still no built-in way (using leaflet) to give a marker an ID. I know I'm a bit late to answering this but hopefully it will help others who stumble upon this question. As far as I can tell there are two main issues here:

Problem #1: Unless you save your markers to an object or map, described below, there is no easy programmatic way of accessing them later on. For example - A user clicks something OUTSIDE the map that corresponds to a marker INSIDE the map.

Problem #2: When a user clicks on a marker INSIDE the map, there is no built in way to retrieve the ID of that marker and then use it to highlight a corresponding element or trigger an action OUTSIDE the map.

Solutions

Using a one or more of these options will help address the issues described above. I'll start with the one mentioned in the previous answer. Here is the working pen, which holds all the code found below.

Option #1: Save each marker, using a hardcoded or dynamic ID, inside an object -

// Create or retrieve the data
var data = [
    {
      name: 'Bob',
      latLng: [41.028, 28.975],
      id: '2342fc7'
    }, {...}, {...}
];

// Add an object to save markers
var markers = {};

// Loop through the data
for (var i = 0; i < data.length; i++) {
  var person = data[i];

  // Create and save a reference to each marker
  markers[person.id] = L.marker(person.latLng, {
    ...
  }).addTo(map);
}

Similar to the other answer you can now access a single marker by using -

var marker = markers.2342fc7; // or markers['2342fc7']

Option #2:

While leaflet doesn't provide a built-in 'id' option for markers, you can add the an ID to the element directly by accessing ._icon property:

// Create and save a reference to each marker
markers[person.id] = L.marker(person.latLng, {...}).addTo(map);

// Add the ID
markers[person.id]._icon.id = person.id;

Now when you handle click events, it's easy as pie to get that marker's ID:

$('.leaflet-marker-icon').on('click', function(e) {
   // Use the event to find the clicked element
   var el = $(e.srcElement || e.target),
       id = el.attr('id');

    alert('Here is the markers ID: ' + id + '. Use it as you wish.')
});

Option #3:

Another approach would be use the layerGroup interface. It provides a method, getLayer, that sounds like it would be perfect get our markers using an ID. However, at this time, Leaflet does not provide any way to specify a custom ID or name. This issue on Github discusses how this should be done. However you can get and save the auto-generated ID of any Marker (or iLayer for that matter) like so:

var group = L.layerGroup()

people.forEach(person => {
    // ... create marker
    group.addLayer( marker );
    person.marker_id = group.getLayerId(marker)
})

Now that we have every marker's ID saved with each backing object in our array of data we can easily get the marker later on like so:

group.getLayer(person.marker_id)

See this pen for a full example...

Option #4:

The cleanest way to do this, if you have the time, would be to extend the leaflet's marker class to handle your individual needs cleanly. You could either add an id to the options or insert custom HTML into the marker that has your id/class. See the documentation for more info on this.

You can also you use the circleMarker which, in the path options, you will see has an option for className which can be nice for styling groups of similar markers.

Styling:

Almost forgot that your original question was posed for the purpose of styling... simply use the ID to access individual elements:

.leaflet-marker-icon#2342fc7 { ... }

Conclusion

I'll also mention that layer and feature groups provide another great way to interface with markers. Here is a question that discusses this a bit. Feel free to tinker with, or fork either the first or second pen and comment if I've missed something.

8
votes

An easy way to do this is to add all the markers to a list with a unique id.

var markersObject = {};
markersObject["id1"] = marker1;
markersObject["id2"] = marker2;
markersObject["id3"] = marker3;

If the list of restaurants have a property in the html element of a single restaurant that corresponds to the id of the added marker. Something like:

<a href="#" id="singleRestaurantItem" data-restaurantID="id1" data-foo="bar">Click</a>

Then add the click event where you will pass the id of the restaurant (in this case "data-restaurantID") and do something like:

markersObject["passedValueFromTheClickedElement"].openPopup();

This way once you click on the item in the list a markers popup will open indicating where on the map is the restaurant located.

5
votes
var MarkerIcon = L.Icon.extend({
    options: {
        customId: "",
        shadowUrl: 'leaf-shadow.png',
        iconSize: [64, 64],
        shadowSize: [50, 64],
        iconAnchor: [22, 94],
        shadowAnchor: [4, 62],
        popupAnchor: [-3, -76]
    }
});

var greenIcon = new MarkerIcon({iconUrl: "/resources/images/marker-green.png"}),            
    redIcon = new MarkerIcon({iconUrl: "/resources/images/marker-red.png"}),
    orangeIcon = new MarkerIcon({iconUrl: "/resources/images/marker-orange.png"});

var mymap = L.map('mapid').setView([55.7522200, 37.6155600], 13);

L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
    maxZoom: 18,
    id: 'mapbox.streets'
}).addTo(mymap);

// добавить маркер
L.marker([55.7522200, 37.6155600], {customId:"010000006148", icon: greenIcon, title:setMarkerTitle("010000006148")}).addTo(mymap).on('click', markerOnClick);
L.marker([55.7622200, 37.6155600], {customId:"010053166625", icon: redIcon, title: setMarkerTitle("010053166625")}).addTo(mymap).on('click', markerOnClick);

function markerOnClick(e) {
    var customId = this.options.customId;
    document.location.href = "/view/abonents/" + customId;
}

function setMarkerTitle(customId){
    var result = customId;
    result += "\nline2 ";
    result += "\nline3 ";
    return result;
}
4
votes

For my case, I found the best way was to generate and pass a unique ID to L.marker's Options object when I create it.

const newMarker = L.marker([lat, lng], { uniqueID })

You can then add this marker to a leaflet layerGroup.

const newLayerGroup = L.layerGroup().addTo(map);
newLayerGroup.addLayer(newMarker);

You can access the ID with layer.options.uniqueID This allows me to find and manipulate the marker later; all I need is Leaflet's .eachLayer() and the uniqueID.

My backend (Cloud Firestore) already generates unique document ID's, which makes it super easy to sync my Leaflet map and backend in real-time, rather than rebuilding and remounting the entire layerGroup or refreshing the page.

//e.g. a callback which fires whenever a doc has been removed from my db

newLayerGroup.eachLayer((layer) => {
  if (deletedDocID === layer.options.uniqueID) {
    newLayerGroup.removeLayer(layer);
  }
});
0
votes

A fairly straight forward and easy way to accomplish creating an array of clickable markers within a leaflet map object is to manipulate the class list of the created marker by adding a custom incremented class name to each marker. Then it is easy to create a listener and know which marker was clicked. By skipping the active one or not, each has a retrievable click event with a reliable ID.

  // creates markers, each with a leaflet supplied class
  if (length === 1) {
    for (i = 0; i < parks.length; ++i) {
      if (parks[i].parksNumber !== parks.parksNumber)
        L.marker([parks[i].latitude, parks[i].longitude], {
          icon: parks[i].iconMarker
        }).addTo(mymap);
    }
  }

  // select all of the leaflet supplied class
  let markers = document.querySelectorAll(".leaflet-marker-icon");

  // loop through those elements and first assign the indexed custom class
  for (i = 0; i < markers.length; ++i) {
    markers[i].classList.add("marker_" + parks[i].parksNumber);

    // then add a click listener to each one
    markers[i].addEventListener("click", e => {

      // pull the class list
      let id = String(e.target.classList);

      // pull your unique ID from the list, be careful cause this list could 
      // change orientation,  if so loop through and find it
      let parksNumber = id.split(" ");
      parksNumber = parksNumber[parksNumber.length - 1].replace("marker_", "");

      // you have your unique identifier to then do what you want with
      search_Number_input.value = parksNumber;
      HandleSearch();
    });
  }
0
votes

1.) Lets create Marker with unique id...

L.marker([marker.lat, marker.lng],{customID:'some ID',title:marker.title}).on('click', this.markerClick).addTo(mymap);

2.) Go to node_modules@types\leaflet\index.d.ts and add customID?:string;

export interface MarkerOptions extends InteractiveLayerOptions {
    icon?: Icon | DivIcon;
    title?: string;

    ....

    autoPanSpeed?: number;
    customID:string;
}

3.) In the same file add customID to LeafletMouseEvent

export interface LeafletMouseEvent extends LeafletEvent {
  latlng: LatLng;
  layerPoint: Point;
  containerPoint: Point;
  originalEvent: MouseEvent;
  customID:customID
}

4.) Create customID class

export class customID {
  constructor(customID: string);
  customID: number;
} 

5.) Get your marker id in function

markerClick(e){
  console.log(e.sourceTarget.options.customID)
}
-1
votes

Leaflet's className option can allow one to add identifiers to objects:

var onMouseover = function() {
  // returns all members of the specified class
  d3.selectAll(".mySpecialClass")
    .style("opacity", ".1");
};

// add desired class to pointer 
L.circleMarker([46.85, 2.35], {className: "mySpecialClass"})
  .addTo(map).on('mouseover', onMouseover);

// to select the marker(s) with a particular class, just use css selectors
// here's a d3.js solution
d3.selectAll(".mySpecialClass")
  .style("opacity", ".3")