Skip to content

Interaction and Events

The Mapbox GL JS interaction and events system provides a powerful framework for handling user interactions with maps and features. This page documents how to work with the event system, create custom interactions, target specific features, and manage feature state for visual feedback.

For information about UI controls that can be added to the map, see Controls.

Event System Overview

Mapbox GL JS wraps native browser events into map-specific event objects that provide geographic context and additional functionality. The events system is built on top of the Evented class, which provides methods for registering event listeners using on(), once(), and off().

Event Types and Objects

There are three main event object types:

  • MapMouseEvent: For mouse interactions like click, mousemove
  • MapTouchEvent: For touch interactions like touchstart, touchend
  • MapWheelEvent: For wheel events (wheel)
SVG
100%

Basic Event Handling

To listen for map events, use the map.on() method:

// Listen for clicks anywhere on the map
map.on('click', (e) => {
  console.log(`Clicked at ${e.lngLat.lng}, ${e.lngLat.lat}`);
});

// Listen for clicks on a specific layer
map.on('click', 'building-layer', (e) => {
  console.log('Clicked building:', e.features[0].properties);
});

Preventing Default Behaviors

Event objects include a preventDefault() method that stops the map's default handling of the event (such as panning, zooming, or rotation):

map.on('mousedown', (e) => {
  // Prevent the map's drag behavior
  e.preventDefault();
  // Custom behavior...
});

The Interaction System

Mapbox GL JS provides a high-level interaction system that extends beyond basic event handling, allowing for more declarative and feature-specific interactions. This system is built around the InteractionSet class and accessed through the map.addInteraction() method.

Interaction Flow

SVG
100%

Adding Interactions

Interactions are configured using the map.addInteraction(id, config) method:

map.addInteraction('building-click', {
  type: 'click',                                   // Event type
  target: {featuresetId: 'buildings'},             // Target descriptor
  filter: ['>=', ['get', 'height'], 100],          // Optional filter
  handler: (e) => {                                // Event handler
    map.setFeatureState(e.feature, {selected: true});
  }
});

The Interaction configuration object includes:

  • type: The event type ('click', 'mouseenter', etc.)
  • target: Optional target descriptor (layer or featureset)
  • filter: Optional expression to filter features
  • handler: Function that receives an InteractionEvent

InteractionEvent

The InteractionEvent object passed to handlers includes:

  • Standard event properties (type, point, lngLat, originalEvent)
  • feature: The specific feature that was interacted with (as a TargetFeature)
  • id: The interaction ID
  • interaction: The original interaction configuration
  • preventDefault(): Method to stop event propagation

Feature Targeting

Mapbox GL JS has a flexible system for targeting specific features or collections of features for interactions.

Target Descriptors

A target descriptor can be either:

  • A layer target: {layerId: 'building-layer'}
  • A featureset target: {featuresetId: 'buildings', importId?: 'basemap'}

Featuresets are collections of features defined in a style's featuresets property and can span multiple layers or imports.

SVG
100%

Feature Variants

When querying features with target descriptors, the resulting features include a variants property that maps targets to feature variants. When these features are passed to interaction handlers, they're wrapped in a TargetFeature that represents the specific variant that matched the target.

SVG
100%

Feature State Management

Feature state allows for dynamic styling and interaction feedback. States are per-feature key-value pairs that can be used in style expressions.

Managing Feature State

// Set a feature state
map.setFeatureState(feature, {selected: true, hover: false});

// Get current feature state
const state = map.getFeatureState(feature);

// Remove a specific state property
map.removeFeatureState(feature, 'selected');

// Remove all state properties
map.removeFeatureState(feature);

Using Feature State in Styles

Feature states can be referenced in style expressions using the feature-state operator:

map.setPaintProperty('buildings', 'fill-color', [
  'case',
  ['boolean', ['feature-state', 'selected'], false],
  '#ff0000',  // Selected color
  ['boolean', ['feature-state', 'hover'], false],
  '#00ff00',  // Hover color
  '#0000ff'   // Default color
]);

Common Interaction Patterns

Hover Effects

// Add mouseenter interaction
map.addInteraction('building-hover', {
  type: 'mouseenter',
  target: {featuresetId: 'buildings'},
  handler: (e) => {
    map.setFeatureState(e.feature, {hover: true});
  }
});

// Add mouseleave interaction
map.addInteraction('building-unhover', {
  type: 'mouseleave',
  target: {featuresetId: 'buildings'},
  handler: (e) => {
    map.setFeatureState(e.feature, {hover: false});
  }
});

Selection

let selectedFeature = null;

map.addInteraction('building-select', {
  type: 'click',
  target: {featuresetId: 'buildings'},
  handler: (e) => {
    // Deselect previous feature
    if (selectedFeature) {
      map.setFeatureState(selectedFeature, {selected: false});
    }
    
    // Select new feature
    selectedFeature = e.feature;
    map.setFeatureState(selectedFeature, {selected: true});
    
    // Show popup or details
    new mapboxgl.Popup()
      .setLngLat(e.lngLat)
      .setHTML(`<h3>${e.feature.properties.name || 'Building'}</h3>`)
      .addTo(map);
  }
});

Working with Imports

When working with imported styles, you can target featuresets in specific imports:

map.addInteraction('basemap-building-click', {
  type: 'click',
  target: {featuresetId: 'buildings', importId: 'basemap'},
  handler: (e) => {
    // Handle click on buildings from the basemap import
    console.log('Clicked on building in basemap:', e.feature);
  }
});

InteractionSet Implementation

Under the hood, the interaction system is managed by an InteractionSet instance that handles:

  1. Registering and managing interactions
  2. Delegating events to appropriate handlers
  3. Tracking hover state for mouseenter/mouseleave events
  4. Querying features for interaction targets
  5. Preventing interaction duplication

The InteractionSet.handleType() method is responsible for handling all interaction events. For mouseenter/mouseleave events, handleMove() and handleOut() are used to track state changes and fire the appropriate events.

Advanced Topics

Stopping Event Propagation

Interaction handlers can control whether other interactions should process an event by returning a boolean:

map.addInteraction('exclusive-click', {
  type: 'click',
  target: {layerId: 'important-layer'},
  handler: (e) => {
    // Handle the click
    console.log('Handling click exclusively');
    
    // Return true to stop propagation to other interactions
    // Return false (or undefined) to allow other interactions
    return true;
  }
});

Feature Filters

Interactions can include filters to limit which features respond to interactions:

map.addInteraction('tall-building-click', {
  type: 'click',
  target: {featuresetId: 'buildings'},
  // Only trigger for buildings taller than 100 meters
  filter: ['>', ['get', 'height'], 100],
  handler: (e) => {
    console.log('Clicked on tall building:', e.feature);
  }
});

The filter syntax uses the same expression system as Mapbox GL styles.

Feature Namespaces

For complex applications, you can use namespaces to organize features:

map.addInteraction('city-building-click', {
  type: 'click',
  target: {featuresetId: 'buildings'},
  namespace: 'city',
  handler: (e) => {
    console.log('City building namespace:', e.feature.namespace);
    // Handle click on city buildings
  }
});

Supported Event Types

Event TypeDescriptionBased On
mousedownMouse button pressedMouseEvent
mouseupMouse button releasedMouseEvent
clickMouse clickMouseEvent
dblclickDouble clickMouseEvent
mousemoveMouse movementMouseEvent
mouseenterMouse enters a featureMouseEvent (delegated)
mouseleaveMouse leaves a featureMouseEvent (delegated)
mouseoverAlias for mouseenterMouseEvent (delegated)
mouseoutAlias for mouseleaveMouseEvent (delegated)
touchstartTouch beginsTouchEvent
touchendTouch endsTouchEvent
touchcancelTouch is canceledTouchEvent
wheelMouse wheel movementWheelEvent

Implementation Details

The interaction and events system is implemented through several key components:

  1. Map events (in src/ui/events.ts) wrap DOM events with map-specific context
  2. InteractionSet (in src/ui/interactions.ts) manages interactions and delegated events
  3. Feature targeting (in src/util/vectortile_to_geojson.ts) handles feature variants and target features
  4. Query features (in src/source/query_features.ts) performs feature lookups with target descriptors
  5. Feature state is stored in the style system and used for rendering

When an interaction is added with map.addInteraction(), the system:

  1. Creates an entry in the InteractionSet
  2. Sets up event listeners if needed
  3. For hover-related events, sets up mousemove/mouseout delegation

When an event occurs, the system:

  1. Creates a MapMouseEvent/MapTouchEvent
  2. Delegates to InteractionSet.handleType()
  3. Queries features based on target descriptors
  4. Applies filters
  5. Creates TargetFeature instances for matched features
  6. Calls handlers with InteractionEvent objects