Mapbox-gl-js: Layer-specific click listeners get triggered by separate, non-specific listeners

Created on 12 Aug 2019  路  6Comments  路  Source: mapbox/mapbox-gl-js

mapbox-gl-js version:
1.2.0

browser:
Chrome

Steps to Trigger Behavior

Create a simple map

this.map = new mapboxgl.Map({
  container: 'map',
  style: 'mapbox://styles/mapbox/streets-v9',
  zoom: 19, // starting zoom
  center: [13.380702060621635, 52.5220511942754]
});

Let's add an layer to the map.

  this.map.on('load', async () => {
  const controls = new mapboxgl.NavigationControl();
  this.map.addControl(controls, 'top-right');

  this.map.addSource('foo', {
    type: 'geojson',
    data: coords
  });

  this.map.addLayer({
    id: 'points',
    type: 'circle',
    source: 'foo',
    paint: {
      'circle-radius': 5,
      'circle-color': 'hotpink',
    },
  });
});

Now lets add click() functionality to the map. First, detect a click on the layer with the name points

this.map.on('click', 'points', event => {
  console.log('Layer click')
});

Now, detect a click on the map, not on the points layer

this.map.on('click', () => {
  console.log('Basemap click')
});

Expected Behavior

I expect the this.map.on('click', () => {... to be fired only when I do not click on the layer points, hence somewhere next to a point displyed in hotpink on the map.
I expect the this.map.on('click', 'points', event => {... to be fired only when I do click on a point of the layer points.

Actual Behavior

this.map.on('click', 'points', event => {... works as expected.
this.map.on('click', () => {... fires every time I click on the map, even if I click on the points layer. That is unexpected.

Codepen

https://codepen.io/diesdasananas/pen/eqVLyj

bug

All 6 comments

I expect the this.map.on('click', () => {... to be fired only when I do not click on the layer points

events apply to all layers by default, so omitting the layer parameter means the map will listen to clicks on all layers.

However, the demo DOES illustrate a bug where listeners for map-wide clicks will trigger layer-specific listeners

events apply to all layers by default, so omitting the layer parameter means the map will listen to clicks on all layers.

I agree, this is working as intended.

However, the demo DOES illustrate a bug where listeners for map-wide clicks will trigger layer-specific listeners

I couldn't replicate that, when I click on the point it correctly triggers both listeners, and when I click outside the point it correctly only triggers the map wide listener.

馃憤 can no longer reproduce-- might've been a mistake on my end. closing

@andrewharvey @peterqliu

Why does clicking on the point layer trigger a click on the basemap und subsequent functions that may be called, when the basemap is clicked? I dont think that this is expected behaviour. I would think that only the point layer click event gets fired when I click on the point layer. What if I have functions that I only want to call, when I do not click on a layer, but on the basemap (i.e. setting a marker where I clicked?). I would add this functionality in the click event of the basemap

this.map.on('click', event => {
  addMarker(event)
});

But with the behaviour demonstrated, the addMarker() function is also called, when I click on the point layer.

That is a possible solution, but it adds complexity when there are a lot of layers

let clickCoords = {};

this.map.on('click', 'points', event => {
  clickCoords = event.point;
  console.log('Layer click')
});

Now, detect a click on the map, not on the points layer

this.map.on('click', () => {
  // check if coords are different, if they are different, execute whatever you need
  if (clickCoords.x !== event.point.x && clickCoords.y !== event.point.y) {
    console.log('Basemap click');
    clickCoords = {};
  }
});

Mapbox GL JS doesn't have a concept of a basemap. A map is a style made up of a number of layers. A listener without any layer will trigger when you click on any layer, exactly so you can then do different actions based on different layers which are clicked on.

A listener with a layer is just a shorthand way of doing a listener with no layer + queryRenderedFeatures.

If you want to know when there is a click outside the layer, then you can do a click listener without a layer and then queryRenderedFeatures to check if the click was on your layer or not.

Was this page helpful?
0 / 5 - 0 ratings