or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdcontrols.mdevents.mdgeographic-utilities.mdindex.mdmap-core.mdui-components.md
tile.json

events.mddocs/

Events and Interactions

Comprehensive event system for handling user interactions, map state changes, and data loading events throughout the Mapbox GL JS library.

Capabilities

Evented Base Class

Base class that provides event handling capabilities to Map, Marker, Popup, and other interactive components.

/**
 * Event system base class providing event handling capabilities
 */
class Evented {
  /**
   * Add event listener
   * @param type - Event type name
   * @param listener - Event handler function
   * @returns Instance for chaining
   */
  on(type: string, listener: Function): this;
  
  /**
   * Remove event listener
   * @param type - Event type name (optional, removes all if not specified)
   * @param listener - Event handler function (optional, removes all for type if not specified)
   * @returns Instance for chaining
   */
  off(type?: string, listener?: Function): this;
  
  /**
   * Add one-time event listener
   * @param type - Event type name
   * @param listener - Event handler function
   * @returns Instance for chaining
   */
  once(type: string, listener: Function): this;
  
  /**
   * Fire an event
   * @param type - Event type name
   * @param data - Event data object
   * @returns Instance for chaining
   */
  fire(type: string, data?: any): this;
  
  /**
   * Check if has listeners for event type
   * @param type - Event type name
   * @returns True if has listeners
   */
  listens(type: string): boolean;
}

Core Event Types

Base event interfaces used throughout the library.

/**
 * Base event object
 */
interface Event {
  type: string;
  target: any;
  originalEvent?: any;
}

/**
 * Error event with error information
 */
interface ErrorEvent extends Event {
  error: Error;
}

Map Mouse Events

Mouse interaction events fired by the Map class.

/**
 * Mouse event fired by map interactions
 */
interface MapMouseEvent extends Event {
  type: 'mousedown' | 'mouseup' | 'click' | 'dblclick' | 'mousemove' | 'mouseover' | 'mouseout' | 'contextmenu';
  target: Map;
  originalEvent: MouseEvent;
  point: Point;
  lngLat: LngLat;
  preventDefault(): void;
  defaultPrevented: boolean;
}

/**
 * Touch event fired by map interactions
 */
interface MapTouchEvent extends Event {
  type: 'touchstart' | 'touchend' | 'touchmove' | 'touchcancel';
  target: Map;
  originalEvent: TouchEvent;
  point: Point;
  lngLat: LngLat;
  points: Point[];
  lngLats: LngLat[];
  preventDefault(): void;
  defaultPrevented: boolean;
}

/**
 * Wheel event fired by map scroll interactions
 */
interface MapWheelEvent extends Event {
  type: 'wheel';
  target: Map;
  originalEvent: WheelEvent;
  preventDefault(): void;
  defaultPrevented: boolean;
}

Usage Examples:

import mapboxgl from 'mapbox-gl';

// Mouse event handling
map.on('click', (e: mapboxgl.MapMouseEvent) => {
  console.log(`Clicked at: ${e.lngLat.lng}, ${e.lngLat.lat}`);
  console.log(`Screen coordinates: ${e.point.x}, ${e.point.y}`);
});

map.on('mousemove', (e: mapboxgl.MapMouseEvent) => {
  // Update cursor coordinates display
  document.getElementById('coordinates').textContent = 
    `${e.lngLat.lng.toFixed(4)}, ${e.lngLat.lat.toFixed(4)}`;
});

// Touch event handling
map.on('touchstart', (e: mapboxgl.MapTouchEvent) => {
  console.log(`Touch started with ${e.points.length} fingers`);
  e.points.forEach((point, index) => {
    console.log(`Touch ${index}: ${point.x}, ${point.y}`);
  });
});

// Double-click to zoom
map.on('dblclick', (e: mapboxgl.MapMouseEvent) => {
  e.preventDefault(); // Prevent default zoom
  map.flyTo({
    center: e.lngLat,
    zoom: map.getZoom() + 1
  });
});

// Context menu
map.on('contextmenu', (e: mapboxgl.MapMouseEvent) => {
  e.preventDefault();
  showContextMenu(e.point, e.lngLat);
});

Map Data Events

Events related to data loading, style changes, and rendering.

/**
 * Data-related events fired during map operations
 */
interface MapDataEvent extends Event {
  type: 'data' | 'dataloading' | 'sourcedata' | 'sourcedataloading' | 'styledata' | 'styledataloading';
  target: Map;
  dataType?: 'source' | 'style';
  isSourceLoaded?: boolean;
  sourceId?: string;
  sourceDataType?: 'metadata' | 'content' | 'visibility' | 'idle';
  tile?: any;
  coord?: { x: number; y: number; z: number };
}

/**
 * Box zoom event
 */
interface MapBoxZoomEvent extends Event {
  type: 'boxzoomstart' | 'boxzoomend' | 'boxzoomcancel';
  target: Map;
  originalEvent?: MouseEvent;
  boxZoomBounds?: LngLatBounds;
}

Usage Examples:

// Data loading events
map.on('sourcedata', (e: mapboxgl.MapDataEvent) => {
  if (e.sourceId && e.isSourceLoaded) {
    console.log(`Source ${e.sourceId} finished loading`);
  }
});

map.on('data', (e: mapboxgl.MapDataEvent) => {
  if (e.dataType === 'source') {
    console.log('Source data changed');
  } else if (e.dataType === 'style') {
    console.log('Style data changed');
  }
});

// Style loading
map.on('styledata', (e: mapboxgl.MapDataEvent) => {
  console.log('Style data loaded');
  // Add custom layers after style loads
  if (!map.getLayer('custom-layer')) {
    map.addLayer({
      id: 'custom-layer',
      type: 'fill',
      source: 'custom-source',
      paint: { 'fill-color': '#088' }
    });
  }
});

// Box zoom events
map.on('boxzoomstart', (e: mapboxgl.MapBoxZoomEvent) => {
  console.log('Box zoom started');
});

map.on('boxzoomend', (e: mapboxgl.MapBoxZoomEvent) => {
  console.log('Box zoom ended:', e.boxZoomBounds);
});

Map State Events

Events related to map view changes, loading states, and errors.

/**
 * Map lifecycle and state events
 */
type MapEventType = 
  // Lifecycle
  | 'load' | 'idle' | 'remove' | 'render'
  // View changes
  | 'movestart' | 'move' | 'moveend'
  | 'dragstart' | 'drag' | 'dragend'
  | 'zoomstart' | 'zoom' | 'zoomend'
  | 'rotatestart' | 'rotate' | 'rotateend'
  | 'pitchstart' | 'pitch' | 'pitchend'
  // Resize
  | 'resize'
  // Errors
  | 'error' | 'webglcontextlost' | 'webglcontextrestored';

Usage Examples:

// Map lifecycle events
map.on('load', () => {
  console.log('Map fully loaded');
  // Safe to add sources and layers
  initializeMapData();
});

map.on('idle', () => {
  console.log('Map finished rendering and is idle');
  // All tiles loaded, animations complete
});

// View change events
map.on('movestart', () => {
  console.log('Map started moving');
  showLoadingIndicator();
});

map.on('moveend', () => {
  console.log('Map finished moving');
  hideLoadingIndicator();
  updateVisibleData();
});

map.on('zoomend', () => {
  const zoom = map.getZoom();
  console.log(`Zoom level: ${zoom.toFixed(2)}`);
  
  // Show/hide layers based on zoom
  if (zoom > 10) {
    map.setLayoutProperty('detailed-layer', 'visibility', 'visible');
  } else {
    map.setLayoutProperty('detailed-layer', 'visibility', 'none');
  }
});

// Error handling
map.on('error', (e: mapboxgl.ErrorEvent) => {
  console.error('Map error:', e.error);
  showErrorMessage(e.error.message);
});

Layer and Feature Events

Events specific to layer interactions and feature queries.

/**
 * Layer-specific mouse events
 */
interface MapLayerMouseEvent extends MapMouseEvent {
  features?: MapboxGeoJSONFeature[];
}

/**
 * Layer-specific touch events
 */
interface MapLayerTouchEvent extends MapTouchEvent {
  features?: MapboxGeoJSONFeature[];
}

Usage Examples:

// Layer-specific events
map.on('click', 'points-layer', (e: mapboxgl.MapLayerMouseEvent) => {
  if (e.features && e.features.length > 0) {
    const feature = e.features[0];
    console.log('Clicked feature:', feature.properties);
    
    // Show popup with feature info
    new mapboxgl.Popup()
      .setLngLat(e.lngLat)
      .setHTML(`<h3>${feature.properties.name}</h3>`)
      .addTo(map);
  }
});

// Hover effects
map.on('mouseenter', 'interactive-layer', (e: mapboxgl.MapLayerMouseEvent) => {
  map.getCanvasContainer().style.cursor = 'pointer';
  
  if (e.features && e.features.length > 0) {
    const feature = e.features[0];
    map.setFeatureState(
      { source: 'data-source', id: feature.id },
      { hover: true }
    );
  }
});

map.on('mouseleave', 'interactive-layer', () => {
  map.getCanvasContainer().style.cursor = '';
  
  // Clear all hover states
  map.removeFeatureState({ source: 'data-source' }, 'hover');
});

Custom Events

You can fire custom events on Evented objects.

/**
 * Fire custom events with data
 */
interface CustomEventData {
  [key: string]: any;
}

Usage Examples:

// Custom event handling
class CustomMapController extends mapboxgl.Evented {
  private map: mapboxgl.Map;
  
  constructor(map: mapboxgl.Map) {
    super();
    this.map = map;
  }
  
  performCustomAction(data: any) {
    // Do custom work
    console.log('Performing custom action with:', data);
    
    // Fire custom event
    this.fire('customaction', { data, timestamp: Date.now() });
  }
}

// Usage
const controller = new CustomMapController(map);

controller.on('customaction', (e) => {
  console.log('Custom action fired:', e.data, 'at', e.timestamp);
});

controller.performCustomAction({ type: 'analysis', value: 42 });

Event Management Patterns

Event Cleanup

// Proper event cleanup
class MapManager {
  private map: mapboxgl.Map;
  private handlers: { [key: string]: Function } = {};
  
  constructor(container: string) {
    this.map = new mapboxgl.Map({ container });
    this.setupEventHandlers();
  }
  
  private setupEventHandlers() {
    this.handlers.click = (e: mapboxgl.MapMouseEvent) => {
      console.log('Map clicked:', e.lngLat);
    };
    
    this.handlers.move = () => {
      console.log('Map moved');
    };
    
    // Add handlers
    this.map.on('click', this.handlers.click);
    this.map.on('move', this.handlers.move);
  }
  
  destroy() {
    // Remove all handlers
    Object.keys(this.handlers).forEach(eventType => {
      this.map.off(eventType, this.handlers[eventType]);
    });
    
    this.map.remove();
  }
}

Event Delegation

// Handle events for multiple layers
const layerIds = ['layer1', 'layer2', 'layer3'];

function handleLayerClick(e: mapboxgl.MapLayerMouseEvent) {
  const layerId = e.target.id;
  console.log(`Clicked on layer: ${layerId}`);
  
  if (e.features && e.features.length > 0) {
    const feature = e.features[0];
    handleFeatureClick(feature, layerId);
  }
}

// Add same handler to multiple layers
layerIds.forEach(layerId => {
  map.on('click', layerId, handleLayerClick);
});

Types

// Event type definitions
type MapEventType = 
  // Mouse events
  | 'mousedown' | 'mouseup' | 'click' | 'dblclick' | 'mousemove' | 'mouseover' | 'mouseout' | 'contextmenu'
  // Touch events  
  | 'touchstart' | 'touchend' | 'touchmove' | 'touchcancel'
  // Wheel events
  | 'wheel'
  // Map lifecycle
  | 'load' | 'idle' | 'remove' | 'render'
  // View changes
  | 'movestart' | 'move' | 'moveend'
  | 'dragstart' | 'drag' | 'dragend' 
  | 'zoomstart' | 'zoom' | 'zoomend'
  | 'rotatestart' | 'rotate' | 'rotateend'
  | 'pitchstart' | 'pitch' | 'pitchend'
  // Data events
  | 'data' | 'dataloading' | 'sourcedata' | 'sourcedataloading' | 'styledata' | 'styledataloading'
  // Box zoom
  | 'boxzoomstart' | 'boxzoomend' | 'boxzoomcancel'
  // Resize and errors
  | 'resize' | 'error' | 'webglcontextlost' | 'webglcontextrestored';

// Event object type mapping
type MapEventOf<T extends MapEventType> = 
  T extends 'click' | 'dblclick' | 'mousedown' | 'mouseup' | 'mousemove' | 'mouseover' | 'mouseout' | 'contextmenu' 
    ? MapMouseEvent
  : T extends 'touchstart' | 'touchend' | 'touchmove' | 'touchcancel'
    ? MapTouchEvent  
  : T extends 'wheel'
    ? MapWheelEvent
  : T extends 'data' | 'dataloading' | 'sourcedata' | 'sourcedataloading' | 'styledata' | 'styledataloading'
    ? MapDataEvent
  : T extends 'boxzoomstart' | 'boxzoomend' | 'boxzoomcancel'
    ? MapBoxZoomEvent
  : T extends 'error'
    ? ErrorEvent
  : Event;