← Files MapboxARCHIVED FILE
skills/mapbox-web-performance-patterns/references/interactions.md
2.49 KB · Sep 30, 2026 · 23:11 UTC
# Optimize Map Interactions
**Problem:** Unthrottled event handlers cause performance degradation.
## Anti-Pattern: Expensive Operations on Every Event
```javascript
// ❌ BAD: Runs ~60 times per second during pan (once per render frame)
map.on('move', () => {
updateVisibleFeatures(); // Expensive query
fetchDataFromAPI(); // Network request
updateUI(); // DOM manipulation
});
```
## Debounce/Throttle Events
```javascript
// ✅ GOOD: Throttle during interaction, finalize on idle
let throttleTimeout;
// Lightweight updates during move (throttled)
map.on('move', () => {
if (throttleTimeout) return;
throttleTimeout = setTimeout(() => {
updateMapCenter(); // Cheap update
throttleTimeout = null;
}, 100);
});
// Expensive operations after interaction stops
map.on('moveend', () => {
updateVisibleFeatures();
fetchDataFromAPI();
updateUI();
});
```
## Optimize Feature Queries
```javascript
// ❌ BAD: Query all features (expensive with many layers)
map.on('click', (e) => {
const features = map.queryRenderedFeatures(e.point);
console.log(features); // Could be 100+ features from all layers
});
// ✅ GOOD: Query specific layers only
map.on('click', (e) => {
const features = map.queryRenderedFeatures(e.point, {
layers: ['restaurants', 'shops'] // Only query these layers
});
if (features.length > 0) {
showPopup(features[0]);
}
});
// ✅ For touch targets or fuzzy clicks: Use a bounding box
map.on('click', (e) => {
const bbox = [
[e.point.x - 5, e.point.y - 5],
[e.point.x + 5, e.point.y + 5]
];
const features = map.queryRenderedFeatures(bbox, {
layers: ['restaurants'],
filter: ['==', ['get', 'type'], 'pizza'] // Further narrow results
});
});
```
## Batch DOM Updates
```javascript
// ❌ BAD: Update DOM for every feature
map.on('mousemove', 'restaurants', (e) => {
e.features.forEach((feature) => {
document.getElementById(feature.id).classList.add('highlight');
});
});
// ✅ GOOD: Batch updates with requestAnimationFrame
let pendingUpdates = new Set();
let rafScheduled = false;
map.on('mousemove', 'restaurants', (e) => {
e.features.forEach((f) => pendingUpdates.add(f.id));
if (!rafScheduled) {
rafScheduled = true;
requestAnimationFrame(() => {
pendingUpdates.forEach((id) => {
document.getElementById(id).classList.add('highlight');
});
pendingUpdates.clear();
rafScheduled = false;
});
}
});
```
**Impact:** 60 FPS maintained during interaction vs 15-20 FPS without optimization
SHA-256: 29e41ac1613ea5a49e5be711eba5dc92638bfb2cf9e4587fd291c2ae9e8ac0e8