Leaflet: map is not removed completely on `map.remove()`: `Uncaught TypeError: Cannot read property '_leaflet_pos' of undefined`
How to reproduce
- Leaflet version I’m using: 1.2.0
- Browser (with version) I’m using: Chrome Version 60.0.3112.113
- It works fine in Firefox and Safari (havn’t tested in IE, Edge)
- OS/Platform (with version) I’m using: macOS Sierra
- add map in div element and add layer
this.leafletMap = new L.Map( <element> , {
zoomControl: true,
dragging: this.isInDragMode,
touchZoom: false,
scrollWheelZoom: false,
doubleClickZoom: false,
tap: false,
}
L.tileLayer( 'http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
} ).addTo( this.leafletMap );
- Remove the map on some user action
if (this.leafletMap ){
this.leafletMap.eachLayer(function(layer){
layer.remove();
});
this.leafletMap.remove();
this.leafletMap = null;
}
What behaviour I’m expecting and which behaviour I’m seeing
- Post removal of the map, it removes the map from the element, however, if I do double click on the div, it throws error -
Uncaught TypeError: Cannot read property '_leaflet_pos' of undefined
It seems like DOM element is still holding the event listeners even though the map and layers are removed.
Minimal example reproducing the issue
- this example is as simple as possible
- this example does not rely on any third party code
Using http://playground-leaflet.rhcloud.com/ or any other jsfiddle-like site.
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 11
- Comments: 39 (13 by maintainers)
@spydmobile here goes, this is what i did in a slightly modified form: I have no idea how to post code properly in this f*ing comment field, sorry bout that. I have edited my own comment about 10 times now lol
Hi there – I think I’m experiencing this issue as well. Here’s my basic use-case:
I’m building a viewer component (using the Leaflet-IIIF plugin, but I don’t think that impacts anything here) for objects with multiple pages / surfaces as opposed to displaying an actual map. When the viewer loads, there is a series of thumbnails which the user can click to update which view of an object is displayed in the central area of the UI.
When the user changes the view, I’m calling
map.remove()
before setting up a new map for the new view. The new map is created on the same DOM element as the old one (a div with an ID), and I’m not modifying the DOM in any way outside of Leaflet.On the initial view everything works fine. But after calling
map.remove()
and showing a new view, the console complains:Cannot read property '_leaflet_pos' of undefined
whenever the map is dragged or zoomed.I can try to post a minimal example at some point, but this seems to be the same problem. This error comes up in Chrome but not in Firefox.
I found another workaround. Initializing your map with the undocumented option
touchExtend : false
deactivates the problematic handler, so no more exceptions. I don’t really know what features i’m losing by doing that but looking at the code it could be some extended gestures for mobile or touch screens?? In any case in my app everything seems to work just fine.Hi. I have reproduced this error in a fiddle. simply put, if you create a map inside a div element, then use the remove method, then repopulate the map on the same div, every map move will then generate an error Uncaught TypeError: Cannot read property ‘_leaflet_pos’ of undefined.
To reproduce, open my fiddle, click remove map, click place map, then open the console and move the map. http://jsfiddle.net/spydmobile/5hmadjnk/
Note, it only happens in Chorme, not in FF
Here’s a SSCCE: https://jsfiddle.net/0oafw694/1/
Basically, running the following code …
… leaves two event listeners attached:
I guess,
zoomanim/_createAnimProxy
is handled viaunload/_destroyAnimProxy
, and thus no problem. But themoveend/_panInsideMaxBounds
needs to be unregistered. I’ll prepare a PR…I think i might have found the solution:
The Map container div still has some events that are fired even after
map.off
andmap.remove
.In my case the map has properties that start with
_leaflet_
and some of those functions i found to be on the map itself in property"map._leaflet_events".
Those seem to be attached as
pointerdown
,pointermove
and such but the names of the Properties is likemap._leaflet_touchstarttouchstart32
etc.I found that if i iterate those and remove them manually (using
removeEventListener
then nulling and deleting the property itself for good measure), i can reuse the div for another map. This also put an end to the memory leaks i was seeing.I cannot post code here, but if you search the leaflet source for
POINTER_DOWN
you’ll see the events that get attached, and know how to detach them.Closing per above comment.
I just ended up creating a div for the map that has a dynamic id, so when I have to reuse the div, I remove() the existing map in order to release memory (even so there are some events still going arround) and then redraw the div with a diferent id so I create a new map in it.
I also store all of my maps in an object, so I can manipulate them according to its id (I have more than one map visible some times, all with dynamic ids)
@IvanSanchez I’m not sure it’s the same issue, but could be related. When you destroy the map when zoom animation is in progress, you get the same error:
Uncaught TypeError: Cannot read property '_leaflet_pos' of undefined
.I tried to look inside the code and found out that inside
Map._animateZoom()
there is a line:setTimeout(Util.bind(this._onZoomTransitionEnd, this), 250);
If I understand it enough, this timeout is not destroyed when map is removed, so the functionMap._onZoomTransitionEnd
is always called. It can be your “missing a function call here or there”.And the simplified call tree
this._onZoomTransitionEnd
->this._move
->this._getNewPixelOrigin
->this._getMapPanePos
->getPosition(this._mapPane)
->return el._leaflet_pos
fails, becausethis._mapPane
is undefined.Maybe this case could be fixed, if you wrap the
this._move
andthis._moveEnd
calls into theif (this._mapPane) {}
condition, but I didn’t test if it has some other consequences.Replace this:
with this:
Now the question is: What are the events for which
removePointerListener
is not called? Maybe we’re missing a function call here or there.Yes. It’s nearly impossible to detect whether a system has a touchscreen, so if the browser supports pointer events, the assumption is that they will be used.
Hey, don’t despair, this is great investigation work! 😄
Yeah, i know that function and i see it gets called, but not for all events.
I think the problem is that the code attaches them as “pointermove” etc to the dom, but the property names are “touchstart” etc. Also the word “touchstart” is seen twice in the property name, maybe an unexpected doube concat of the id and event name?
Also should those “pointer” events even be attached on Windows 10 without Touchscreen and in Chrome? Unfortunatly, i don’t know enough about leaflets inner workings to provide a real fix 😦
@FLoibl This is a very good investigation 👍
Could you please add some logging around… ? https://github.com/Leaflet/Leaflet/blob/5161140e952969c5da27751b79154a2c93f53bfa/src/dom/DomEvent.Pointer.js#L39 and https://github.com/Leaflet/Leaflet/blob/fe9e0f2333888e8c02b9e7f83bf337d91006ed0a/src/dom/DomEvent.js#L133
Those should be running for every event when a
L.Map
is destroyed, and should be doing the same thing that you’re doing, but I wonder why it doesn’t work as expected.Yes spydmobile thank you for the example, this is the same error I am seeing in Chrome as I reported above