question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Canvas renderer mouseover event precedence is incorrect when features have overlapping pixels

See original GitHub issue

How to reproduce

  • Leaflet version I’m using: 1.0.1
  • Browser I’m using: Chrome
  • OS/Platform I’m using: Windows 10

What behaviour I’m expecting and which behaviour I’m seeing

I expect that when two features render with overlapping pixels (which does not necessarily mean overlapping geometries) the feature that renders on top will be the only feature to receive mouseover events. The default SVG renderer handles overlapping features in this way. The canvas renderer triggers mouseover events on all layers that contain the mouseover point. It’s a bit hard to put into words so for the sake of brevity here are two gifs

SVG (expected): mouseover_correct

Canvas: (unexpected) mouseover_bug

I believe that this was introduced (or at least made more apparent) by https://github.com/Leaflet/Leaflet/commit/609a79279272c88f1089bc2119d309b18c169110 which removes the break statement.

Minimal example reproducing the issue

Possible solution

I would be willing to put up a PR for this but I don’t know how. I tested these changes by directly modifying my local copy of leaflet.

	_handleMouseHover: function(e, point) {
	    var id, layer, drawnLayerKeys;
            // reverse the keys so that we check layers from the top down 
	    drawnLayerKeys = Object.keys(this._drawnLayers).concat().reverse();
	    for (var i = 0; i < drawnLayerKeys.length; i++) {
	        id = drawnLayerKeys[i];
	        layer = this._drawnLayers[id];
	        if (layer.options.interactive && layer._containsPoint(point)) {
	            L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor
	            if (this._hoveredLayer && (this._hoveredLayer._leaflet_id.toString() !== id.toString())) {
                        // If we find a match but another layer is already 'hovered' 
                        // then trigger mouseout on that layer 
	                this._fireEvent([this._hoveredLayer], e, 'mouseout');
	            }
	            this._fireEvent([layer], e, 'mouseover');
	            this._hoveredLayer = layer;
                   // MUST break so that lower layer mouseover events are not triggered
	            break;
	        }
	    }

	    if (this._hoveredLayer) {
	        this._fireEvent([this._hoveredLayer], e);
	    }
	}

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:6 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
jbccollinscommented, Nov 11, 2016

@perliedman @IvanSanchez Thanks for the quick turnaround on this guys! The hovering in my fiddle works great now!

0reactions
balcisericommented, Nov 11, 2016
_handleMouseHover: function (e, point) {
            var id, layer;
            for (id in this._drawnLayers) {
                layer = this._drawnLayers[id];
                if (layer.options.interactive && layer._containsPoint(point)) {
                    L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor
                    if (this._hoveredLayer && this._hoveredLayer !== layer) {
                        this._fireEvent([this._hoveredLayer], e, 'mouseout');
                    }
                    this._fireEvent([layer], e, 'mouseover');
                    this._hoveredLayer = layer;
                }
            }

            if (this._hoveredLayer) {
                this._fireEvent([this._hoveredLayer], e);
            }
        }

This is what i’ve been using since https://github.com/Leaflet/Leaflet/issues/4495 , it’s not great since it keeps firing the mouseout and mouseover events but at least in my app it’s working because it’s firing the mouseout events that I really needed. https://playground-leaflet.rhcloud.com/rix/edit?html,js,console,output

Read more comments on GitHub >

github_iconTop Results From Across the Web

PIXI.Container - PixiJS API Documentation
PIXI.Container. Container is a general-purpose display object that holds children. It also adds built-in support for advanced rendering features ...
Read more >
mousemove with two layers on top of each other (overlapping)
With events like mousemove it returns features in reverse layer order (from "highest" to "lowest"). So the way to do this is:
Read more >
Scene - Cesium Documentation
Exceptions occurring in render are always caught in order to raise the renderError event. If this property is true, the error is rethrown...
Read more >
Documentation - a JavaScript library for interactive maps
Returns the projected pixel coordinates of the top left point of the map layer (useful in custom layer and overlay implementations). getPixelWorldBounds(<Number> ...
Read more >
JSDoc: Class: Canvas - Fabric.js
When true, canvas is scaled by devicePixelRatio for better rendering on retina screens ... When false objects are brought to top and rendered...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found