0

I'm working on a route visualization tool using Leaflet.js to map Amtrak train routes. I'm loading station coordinates from a JSON array and creating markers with custom tooltips showing station names, cities, and codes.

Here's the core of my Javascript code:

var stationData = [
  { name: 'Chicago Union Station', code: 'CHI', lat: 41.8789, lng: -87.6400 },
  { name: 'Denver Union Station', code: 'DEN', lat: 39.7527, lng: -104.9994 },
  { name: 'Salt Lake City', code: 'SLC', lat: 40.7608, lng: -111.8910 }
  // ... more
];

stationData.forEach(function(station) {
  var marker = L.marker([station.lat, station.lng]).addTo(map);
  var popupContent = `<strong>${station.name}</strong><br>Code: ${station.code}`;

  marker.bindTooltip(popupContent, {
    permanent: false,
    direction: 'top',
    offset: [0, -10],
    className: 'station-tooltip'
  });
});

The tooltips sometimes:

  • Fail to render on specific zoom levels (especially when clustered)
  • Don’t reappear after being closed
  • Cause undefined values in popupContent even though the JSON data looks fine

Things I’ve tried:

  • Wrapping the content generation in setTimeout (no improvement)
  • Manually calling marker.openTooltip() after bindTooltip() (breaks hover logic)
  • Debugging station.code values — confirmed they are not undefined

Here's a working map where I’m trying to replicate this behavior:
Amtrak Routes Map

My question:
Is there a common reason Leaflet tooltips silently fail when used in a data loop?

  • Could DOM rendering timing affect dynamic tooltips?
  • Should I be pre-processing the content strings before binding to avoid hidden async issues?

If it helps, I’m also using:

  • Leaflet 1.9.4
  • jQuery 3.6 (for other DOM tasks, not here)
  • Vanilla JS (no React/Vue)

2 Answers 2

0

I’ve worked on something similar before using Leaflet and ran into a comparable issue with tooltips behaving inconsistently, especially after dynamic layer changes.

In your code, this part looks fine at first glance:

var popupContent = `<strong>${station.name}</strong><br>Code: ${station.code>`;
marker.bindTooltip(popupContent, { ... });

But if station.name or station.code is ever undefined (even briefly), the tooltip won't bind correctly. The bindTooltip call doesn't always throw an error — it just silently fails or renders blank.

Here are a few things you might try:

  1. Log values before binding:

    console.log(station.name, station.code);
    

    If even one is undefined, Leaflet may skip rendering the tooltip on some zoom levels.

  2. Use a conditional fallback:

    var popupContent = `<strong>${station.name || 'Unknown Station'}</strong><br>Code: ${station.code || 'N/A'}`;
    
  3. Clear previous layers fully: If you're switching routes or re-rendering markers on zoom, make sure to fully remove existing tooltips. Sometimes .removeLayer() alone doesn’t fully detach internal handlers.

  4. Try forcing the tooltip update: I've seen cases where using marker.unbindTooltip() before bindTooltip() helps reset the DOM state, especially during fast redraws or on mobile.

Also, double-check that the station-tooltip class isn’t interfering via CSS. I had a bug where display: none was being inherited from a global style unexpectedly.

Let me know if you’ve already tried these — happy to dig deeper if you post a bit more of the layer-switching logic.

Sign up to request clarification or add additional context in comments.

Comments

0
// Ensure map and stationData are properly defined before this block
if (Array.isArray(stationData) && typeof L !== 'undefined' && typeof map !== 'undefined') {
  stationData.forEach(function(station) {
    if (
      station &&
      typeof station.lat === 'number' &&
      typeof station.lng === 'number' &&
      station.name &&
      station.code
    ) {
      var marker = L.marker([station.lat, station.lng]).addTo(map);

      var popupContent = `<strong>${station.name}</strong><br>Code: ${station.code}`;

      // Use bindPopup for click interaction and bindTooltip for hover, if needed
      marker.bindTooltip(popupContent, {
        permanent: false,
        direction: 'top',
        offset: [0, -10],
        className: 'station-tooltip',
        sticky: true // helps tooltip follow the mouse and stay visible
      });

      // Optionally bind a popup for more persistent interaction
      marker.bindPopup(popupContent);
    } else {
      console.warn('Invalid station data:', station);
    }
  });
} else {
  console.error('stationData or map is undefined, or Leaflet is not loaded.');
}

Ensures each station has valid lat, lng, name, and code.

Prevents undefined from showing up in the tooltip due to missing fields.

Adds checks to avoid silent failures or breaking when stationData or map isn't ready.

Added sticky: true to help tooltips behave more reliably during hover (especially on mobile or when zooming).

Optionally added bindPopup() for persistent interaction on click — sometimes better UX than tooltips alone.

Clustered Markers Compatibility (Optional) If you're using marker clustering (like Leaflet.markercluster), tooltips might not show until the cluster is expanded.

In that case, instead of adding the marker directly to map, you should add it to the cluster group:

var markerCluster = L.markerClusterGroup();
// Inside the loop:
markerCluster.addLayer(marker);
// After the loop:
map.addLayer(markerCluster);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.