'How to add "click" listener on marker in mapbox-gl-js

I added few markers on the map using folowing this example:

https://www.mapbox.com/mapbox-gl-js/example/geojson-markers/

Now I want to show popup when user clicks on marker image, but i can not find elegant solution. Any help?



Solution 1:[1]

I also had the same problem but I found a workaround without having to create an html element.

I Used getElement() function that return Marker like a HTML Element and after i attach the click event.

      this.service.getAllData().forEach(e => {
        // create MapBox Marker
        const marker = new mapboxgl.Marker().setLngLat([e.lon, e.lat]).addTo(this.map);
        // use GetElement to get HTML Element from marker and add event
        marker.getElement().addEventListener('click', () => {
          alert("Clicked");
        });
      });

Solution 2:[2]

Considering the recent release of MapBox-Gl-js. It can be directly done, just by adding the popup with the marker.

// create a simple popup.
var popup = new mapboxgl.Popup({offset: 25})
    .setText('Construction on the Washington Monument began in 1848.');

// create DOM element for the marker
var el = document.createElement('div');
el.innerHTML = "Marker1";
el.id = 'marker';

// create the marker
new mapboxgl.Marker(el, {offset:[-25, -25]})
    .setLngLat(monument)
    .setPopup(popup) // sets a popup on this marker
    .addTo(map);

Rest you can have your own designed pop-up

var html = '<div class="marker-popup">I am a custom pop-up</div>';

var customPopUp = new mapboxgl.Popup(
    {
       anchor: 'bottom',   // To show popup on top
       offset: { 'bottom': [0, -10] },  // To prevent popup from over shadowing the marker.
       closeOnClick: false   // To prevent close on mapClick.
    }
).setHTML(html); // You can set any valid HTML.

For reference https://www.mapbox.com/mapbox-gl-js/example/set-popup/

One more useful stuff, To attach an on click event on marker, you can do it by attaching click event listener on the marker element like

el.addEventListener('click', () => 
   { 
      alert("Marker Clicked.");
   }
); 

Solution 3:[3]

For starters, in the map.addLayer() function you probably used to add the markers to the map, you need to set "interactive": true in the configuration object.

map.addLayer({
                "id": "YOUR LAYER ID",
                "interactive": true,
                "type": "symbol",
                "source": "YOUR LAYER SOURCE",
                "layout": {
                  "icon-image": "YOUR LAYER ICON IMAGE",
                  "text-font": "Open Sans Semibold, Arial Unicode MS Bold",
                  "text-offset": [0, 0.6],
                  "text-anchor": "top"
                },
                "paint": {
                  "text-size": 12,
                  "icon-size": 1,
                  "icon-opacity": 0
                }
            });

After that, you need to set a click handler on your map along with a check to see if the point is over one of your features (markers).

map.on('click', function(e) {

    console.log(e);

      map.featuresAt(e.point, {radius: 100, layer: 'YOUR MARKER LAYER ID'}, function(err, features) {
            console.log(features[0]);

      });

    });

You can check the docs at their website for more information. Let me know if you have any problems.

Solution 4:[4]

The below snippet is from Mapbox-gl-js example: Display a popup on click

 map.on('click', function (e) {
    var features = map.queryRenderedFeatures(e.point, { layers: ['places'] });

    if (!features.length) {
        return;
    }

    var feature = features[0];
    //Use Feature and put your code
    // Populate the popup and set its coordinates
    // based on the feature found.
    var popup = new mapboxgl.Popup()
        .setLngLat(feature.geometry.coordinates)
        .setHTML(feature.properties.description)
        .addTo(map);
});

   // Use the same approach as above to indicate that the symbols are clickable
   // by changing the cursor style to 'pointer'.

map.on('mousemove', function (e) {
    var features = map.queryRenderedFeatures(e.point, { layers: ['places'] });
    map.getCanvas().style.cursor = (features.length) ? 'pointer' : '';
});

Solution 5:[5]

<link href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.1/mapbox-gl.css" rel="stylesheet" />
<style>
  html, body, #mapContainer {
    height: 100%;
    margin: 0;
  }

  /* Change the cursor to a pointer on hover so the user knows it's clickable */
  .mapboxgl-marker:hover {
    cursor: pointer;
  }

  /* darken the marker on hover */
  .mapboxgl-marker:hover svg > g > g:nth-child(2) {
    fill: #7993a5;
  }

  #info-box {
    position: absolute;
    z-index: 10;
    left: 7px;
    font-family: sans-serif;
    background: #dedede;
    padding: 12px;
    border-radius: 8px;
    font-size: 2rem;
    border: 1px solid #969696;
    bottom: 34px;
  }

</style>
<div id="mapContainer"></div>
<div id="info-box">Click a City</div>

<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.1/mapbox-gl.js'></script>
<script>

mapboxgl.accessToken = 'pk.eyJ1IjoiY3dob25nIiwiYSI6IjAyYzIwYTJjYTVhMzUxZTVkMzdmYTQ2YzBmMTM0ZDAyIn0.owNd_Qa7Sw2neNJbK6zc1A';

const map = new mapboxgl.Map({
  container: 'mapContainer',
  style: 'mapbox://styles/mapbox/light-v9',
  center: [-100.52, 37.67],
  zoom: 3.05,
  pitch: 36,
  hash: true,
});

// Add zoom and rotation controls to the map.
map.addControl(new mapboxgl.NavigationControl());

// extend mapboxGL Marker so we can pass in an onClick handler
class ClickableMarker extends mapboxgl.Marker {
  // new method onClick, sets _handleClick to a function you pass in
  onClick(handleClick) {
    this._handleClick = handleClick;
    return this;
  }

  // the existing _onMapClick was there to trigger a popup
  // but we are hijacking it to run a function we define
  _onMapClick(e) {
    const targetElement = e.originalEvent.target;
    const element = this._element;

    if (this._handleClick && (targetElement === element || element.contains((targetElement)))) {
      this._handleClick();
    }
  }
};

const cities = [
  {
    name: 'New York',
    coordinates: [ -73.969145, 40.669116 ],
  },
  {
    name: 'Washington, D.C.',
    coordinates: [ -77.047119, 38.856820 ],
  },
  {
    name: 'Chicago',
    coordinates: [ -87.662659, 41.865470 ],
  },
  {
    name: 'Seattle',
    coordinates: [ -122.266846, 47.591346 ],
  },
  {
    name: 'St. Louis',
    coordinates: [ -90.304871, 38.634036 ],
  },
  {
    name: 'Houston',
    coordinates: [ -95.377808, 29.754840 ],
  },
];

cities.forEach((city) => {
  new ClickableMarker()
    .setLngLat(city.coordinates)
    .onClick(() => { // onClick() is a thing now!
      document.getElementById('info-box')
        .innerHTML = `You clicked ${city.name}!`;
    })
    .addTo(map);
});

</script>

https://bl.ocks.org/chriswhong/8977c0d4e869e9eaf06b4e9fda80f3ab

here is the solution for this.

add click event separately

Hope this may help

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 beginner's_luck
Solution 2 Rahul
Solution 3 JonathanWard
Solution 4 skarfa
Solution 5 AnumR