3

I'm trying to open a popup when clicking on a tooltip of a layer.

I try following code:

function add_layer_rescue_spot() {
    rescue_spot_layer = new L.GeoJSON.AJAX(rescue_spot_path, {
        style: label_style,
        pane: 'label',
        onEachFeature: on_each_rescue_spot_feature,
        pointToLayer: function(feature, latlon) {
            return point_to_label_layer(
                    '<i class="fa fa-plus-circle"></i>',
                    latlon, 'rescue_spot');
        },
    }).addTo(map);

    rescue_spot_layer.on('data:loaded', function() {
        rescue_spot_layer.getLayers().forEach(function(feature) {
            tooltip = feature.getTooltip().getElement();
            tooltip.addEventListener('click', function(e) {
                console.log('test');
                feature.openPopup();
            });
            tooltip.style.pointerEvents = 'auto';
        });
    });
}

function on_each_rescue_spot_feature(feature, layer) {
    rescue_spot_popup(feature, layer);
}

function point_to_label_layer(text, latlon, class_name) {
    var label = String(text);
    return new L.CircleMarker(latlon, {
        radius: 1,
    }).bindTooltip(label, {
        permanent: true,
        direction: 'center',
        className: class_name + '-label',
    }).openTooltip();
}

function rescue_spot_popup(feature, layer) {
    var properties = feature.properties;
    var max_height = parseInt($('#map').height() * 0.95);
    var popup_html = `
            <h5>Test</h5>
            <table class="popup-table">
                <tr>
                    <td class="attrib-name">foo</td>
                    <td class="attrib-value">foo</td>
                </tr>
                <tr>
                    <td class="attrib-name">bar</td>
                    <td class="attrib-value">bar</td>
                </tr>
            </table>`;
    layer.bindPopup(popup_html, {maxHeight: max_height});
}

So when I click on the tooltip, 'test' is logged and the map moves a bit like the popup would be opened. But the popup doesn't show up.

When I open the popup manually in the browser by typing:

rescue_spot_layer.getLayers()[0].openPopup()

the popup opens as expected.

Any ideas what I'm doing wrong?

2
  • Definitions of two functions are missing in your code (point_to_label_layer and rescue_spot_popup). That would help pinponting the reason for popup not showing up.
    – TomazicM
    Commented Jul 25, 2019 at 7:54
  • rescue_spot_popup is a bit vital to this, to provide a complete answer Commented Jul 25, 2019 at 8:40

2 Answers 2

3

There is no need to use onEachFeature function. Since we are dealing with points only, pointToLayer function is enough. There we can create point marker and bind popup and tooltip to it.

If option interactive: true is used when creating tooltip, it will accept mouse clicks. This mouse click will be propagated to marker and so popup will be automatically opened upon clicking on tooltip.

Remark: Making tooltip interactive makes sense only if it is permanently displayed (option permanent: true). Making nonpermanent tooltip interactive will eiher not work (when tooltip is completely outside marker) or will interfere with event processing for marker (when tooltip covers marker).

So code can be greatly simplified:

function add_layer_rescue_spot() {
  rescue_spot_layer = new L.GeoJSON.AJAX(rescue_spot_path, {
    style: label_style,
    pane: 'label',
    pointToLayer: function(feature, latlon) {
      return point_to_label_layer(
               '<i class="fa fa-plus-circle"></i>',
               feature, latlon, 'rescue_spot'
             );
    }
}).addTo(map);

function point_to_label_layer(text, feature, latlon, class_name) {
  var label = String(text);
  var properties = feature.properties;
  var max_height = parseInt($('#map').height() * 0.95);
  var popup_html = `
          <h5>Test</h5>
          <table class="popup-table">
              <tr>
                  <td class="attrib-name">foo</td>
                  <td class="attrib-value">foo</td>
              </tr>
              <tr>
                  <td class="attrib-name">bar</td>
                  <td class="attrib-value">bar</td>
              </tr>
          </table>`;
  myMarker = L.circleMarker(latlon, {radius: 1});
  myMarker.bindPopup(popup_html, {maxHeight: max_height});
  myMarker.bindTooltip(label, {
      interactive: true,
      permanent: true,
      direction: 'center',
      className: class_name + '-label',
  });
  return myMarker;
}
3
  • "If option interactive: true is used when creating tooltip, it will accept mouse clicks. This mouse click will be propagated to marker and so popup will be automatically opened upon clicking on tooltip." - NOTE: this is only true for permanent tooltips. non-permanent ones close themselves before the click is processed, removing any interactivity. In leaflet source, "... !this.options.permanent { events.preclick = this._close; }" and "closeTooltip: function ... this.removeInteractiveTarget ...". I discovered this by adding logging statements to leaflet-src when my tooltips didn't work. Commented Oct 7, 2019 at 17:52
  • ... this is especially annoying for a centered, non-permanent tooltip, as the tooltip blocks the marker underneath from receiving the event, if user clicks directly on the tooltip. I finally realized the solution was to ONLY declare PERMANENT tooltips INTERACTIVE. Then the non-permanent ones (because they are no longer interactive) don't block the event going to their marker underneath. Commented Oct 7, 2019 at 18:03
  • @ToolmakerSteve You are right, making tooltips interactive makes sense only if they are permanent. I didn't bother with that since question was about permanent tooltips. I will edit my answer and mention that.
    – TomazicM
    Commented Oct 7, 2019 at 18:34
0

There are a few optimisations here that might help.

  1. Consolidate your layer processing into the onEachFeature callback, there is no need to do this on data load as well.

  2. Consider making the layer clickable instead of the tooltip. I'm not sure why this works any differently, but it solves your issue in my code base. IMO this is more intuitive for the user anyway, clicking the tooltip will usually be close to the layer itself, so just let the user click your shape.

The following code will put the click event on the layer, effectively making the whole shape clickable.

function add_layer_rescue_spot() {
    rescue_spot_layer = new L.GeoJSON.AJAX(rescue_spot_path, {
        style: label_style,
        pane: 'label',
        onEachFeature: on_each_rescue_spot_feature,
        pointToLayer: function(feature, latlon) {
            return point_to_label_layer(
                    '<i class="fa fa-plus-circle"></i>',
                    latlon, 'rescue_spot');
        },
    }).addTo(map);
}

function on_each_rescue_spot_feature(feature, layer) {

    // NOTE: example of one of my tooltips, OP didn't detail theirs
    var tooltipContent = '<div class="map-paddock-tooltip">';
    tooltipContent += '<strong>Paddock: #' + feature.properties.Id + '</strong></br>';
    tooltipContent += '<strong>Name:</strong>' + feature.properties.Title + '</br>';
    tooltipContent += '<i>click for more info!</i></br>';
    tooltipContent += '</div>';

    var tooltip = L.tooltip({ className: 'll-tooltip' })
        .setContent(tooltipContent);

    layer.bindTooltip(tooltip);
    layer.on('click', function () {
        layer.openPopup();
    });

}

Further considerations

Consider a context menu, instead of a clickable tooltip, if you have multiple functions that you want to enable when the user right clicks on your layer. Context menus the user will intuitively understand how to work with.

In the following screenshot you will see a shape with a tooltip on the left, and a context menu on the right.
Layer with Tooltip and context menu
I inject disabled context menu items to display structured info about the shape with enabled menu items to call back to actions.
Look into aratcliffe/Leaflet.contextmenu on GitHub

  • Great documentation and support for GeoJSON too.
2
  • Thanks. My problem is still, that the layer consists of point geometries without marker. So the user has to click on the single pixel to open the popup. So my idea was to display a permanent tooltip. Maybe it's a better way to design an own marker.
    – nlaan
    Commented Jul 25, 2019 at 9:57
  • Posting the additional code helps with that ;) Your issue really is about how to increase the clickable surface of the marker then. So why not make the markers with a larger radius or add search to your UI. There's even a leaflet plugin for that. Then you have another mechanism to search the points, rather than relying on the click directly. In this case I think a screenshot of what you have now would help attract a more suitable solution. Commented Jul 25, 2019 at 23:03

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.