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 );
if (this.leafletMap ){
this.leafletMap.eachLayer(function(layer){
layer.remove();
});
this.leafletMap.remove();
this.leafletMap = null;
}
Uncaught TypeError: Cannot read property '_leaflet_pos' of undefined
使用http://playground-leaflet.rhcloud.com/或任何其他类似 jsfiddle 的站点。
您好,感谢您花时间报告此错误。
但是,您描述的重现问题的步骤中似乎缺少某些内容。 我按照上述步骤设置了一个游乐场示例: http :
在此示例中,删除地图后我看不到任何错误,因此显然还有更多原因导致您看到的问题。 能否请您提供更多详细信息,以便我们进行跟踪。
@perliedman感谢您的快速响应并将游乐场示例放在一起。 我无法在那个上重现。 尝试更多场景。 虽然我还在研究它,但有几个问题——
如果我在做map.remove()
,我是否需要显式删除图层? 我的猜测是,它也会处理层去除,但你能确认一下。
并且 map.remove() 超时的原因是在示例中您在创建后立即销毁地图,否则不需要用超时括起来。 正确的?
请记住,在使用开源时,_使用_源! :wink: 要回答你的第一个问题,_yes_, remove
将删除图层: https :
我把它放在超时的原因是让我的例子更现实一点,确保瓷砖层实际上用加载的瓷砖等进行了正确初始化。这个例子在没有放入remove
调用的情况下也能正常工作超时,但这似乎有点人为的测试。
您好 - 我想我也遇到了这个问题。 这是我的基本用例:
我正在为具有多个页面/表面的对象构建一个查看器组件(使用Leaflet-IIIF插件,但我认为这不会影响这里的任何内容),而不是显示实际地图。 当查看器加载时,用户可以单击一系列缩略图来更新在 UI 的中央区域中显示的对象视图。
当用户更改视图时,我会在为新视图设置新地图之前调用map.remove()
。 新地图是在与旧地图相同的 DOM 元素上创建的(带有 ID 的 div),并且我不会在 Leaflet 之外以任何方式修改 DOM。
在初始视图中,一切正常。 但是在调用map.remove()
并显示一个新视图后,控制台会抱怨:每当地图被拖动或缩放时,控制台都会抱怨: Cannot read property '_leaflet_pos' of undefined
。
我可以尝试在某个时候发布一个最小的例子,但这似乎是同样的问题。 这个错误在 Chrome 中出现,但在 Firefox 中没有。
@egardner是的,请尝试创建一个重现此问题的示例!
@egardner在以前稳定的电子(Chromium + Node)应用程序上完全相同的问题我不得不恢复到:
"传单": "1.0.0",
"leaflet.markercluster": "1.0.0-rc.1.0"
从 1.2.0 删除错误无法读取未定义的属性“_leaflet_pos”
这也是在 map.remove() 之后,然后在同一 DOM 元素中重新创建地图。 我现在没有时间在短期内创造和示范
我们在尝试销毁地图时也遇到了类似的问题,它似乎在保留引用
我将重申我上面所说的:为了让我们能够对此问题做任何事情,请提供一个重现问题的示例。
你好。 我在小提琴中重现了这个错误。 简单地说,如果你在一个 div 元素中创建一个地图,然后使用 remove 方法,然后在同一个 div 上重新填充地图,那么每次地图移动都会产生一个错误
未捕获的类型错误:无法读取未定义的属性“_leaflet_pos”。
要重现,请打开我的小提琴,单击移除地图,单击放置地图,然后打开控制台并移动地图。
http://jsfiddle.net/spydmobile/5hmadjnk/
注意,它只发生在 Chorme,而不是 FF
是的 spydmobile 谢谢你的例子,这与我在 Chrome 中看到的错误相同,正如我上面报告的那样
我看到了同样的错误,但在一个稍微不同的用例中。 由于invalidateSize
调用,在调整大小时会抛出相同的错误:
Uncaught TypeError: Cannot read property '_leaflet_pos' of undefined
at getPosition (leaflet-src.js:2765)
at NewClass._getMapPanePos (leaflet-src.js:4378)
at NewClass._rawPanBy (leaflet-src.js:4180)
at NewClass.invalidateSize (leaflet-src.js:3509)
at NewClass.<anonymous> (leaflet-src.js:4244)
完整的调用堆栈从_onResize
处理程序开始。 我正在使用react-leaflet
但堆栈跟踪的任何部分都没有指向那个或本地代码是问题所在。 我确实尝试了一些旧版本(例如1.0.3
和1.2.0
),认为我们至少可以将其锁定到特定的1.x.x
版本,但没有运气。
有任何更新吗? 这里有同样的问题,将传单集成到我的应用程序中。 地图被销毁后,dom 元素仍然具有 _leaflet_events 属性,但摆脱这个对象也无济于事。
处理程序函数的上下文似乎已经过时(处理程序 args 中的 _mapPane 私有属性指向一个不存在的元素)。
我也在经历这个。 这是我看到的抛出的异常之一:
https://sentry.io/share/issue/b414c58ea85c44ee9e0e40ad0781883a/
似乎这主要发生在用户使用浏览器后退按钮离开地图时。
我想我可能已经找到了解决方案:
即使在map.off
和map.remove
之后,Map 容器 div 仍然有一些事件会被触发。
在我的情况下,地图具有以_leaflet_
开头的属性,我发现其中一些函数位于地图本身的属性"map._leaflet_events".
这些似乎附加为pointerdown
, pointermove
等,但属性的名称类似于map._leaflet_touchstarttouchstart32
等。
我发现如果我迭代这些并手动删除它们(使用removeEventListener
然后清零并删除属性本身以获得良好的度量),我可以将 div 重用于另一个地图。
这也结束了我看到的内存泄漏。
我无法在此处发布代码,但是如果您搜索POINTER_DOWN
的传单源,您将看到附加的事件,并知道如何分离它们。
我可以在 Chrome 和 FF 上重现这个(在我自己的项目和@spydmobile 提供的 jsfiddle 中)
@FLoibl也许是在其他地方发布的要点、小提琴或一些片段来演示您成功使用的解决方法?
@spydmobile在这里,这是我以稍微修改的形式所做的:
我不知道如何在这个该死的评论字段中正确发布代码,抱歉。
我已经编辑了我自己的评论大约 10 次,哈哈
function removeMap()
{
var leafletCtrl = get_your_own_leaflet_reference_from_somewhere(),
dom = leafletCtrl.getReferenceToContainerDomSomehow();
//This removes most of the events
leafletCtrl.off();
//After this, the dom element should be good to reuse, unfortunatly it is not
leafletCtrl.remove();
var removeDanglingEvents = function(inputObj, checkPrefix)
{
if(inputObj !== null)
{
//Taken from the leaflet sourcecode directly, you can search for these constants and see how those events are attached, why they are never fully removed i don't know
var msPointer = L.Browser.msPointer,
POINTER_DOWN = msPointer ? 'MSPointerDown' : 'pointerdown',
POINTER_MOVE = msPointer ? 'MSPointerMove' : 'pointermove',
POINTER_UP = msPointer ? 'MSPointerUp' : 'pointerup',
POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
for(var prop in inputObj)
{
var prefixOk = checkPrefix ? prop.indexOf('_leaflet_') !== -1 : true, propVal; //if we are in the _leaflet_events state kill everything, else only stuff that contains the string '_leaflet_'
if(inputObj.hasOwnProperty(prop) && prefixOk)
{
//Map the names of the props to the events that were really attached => touchstart equals POINTER_DOWN etc
var evt = [];
if(prop.indexOf('touchstart') !== -1) //indexOf because the prop names are really weird 'touchstarttouchstart36' etc
{
evt = [POINTER_DOWN];
}
else if(prop.indexOf('touchmove') !== -1)
{
evt = [POINTER_MOVE];
}
else if(prop.indexOf('touchend') !== -1)
{
evt = [POINTER_UP, POINTER_CANCEL];
}
propVal = inputObj[prop];
if(evt.length > 0 && typeof propVal === 'function')
{
evt.each(function(domEvent)
{
dom.removeEventListener(domEvent, propVal, false);
});
}
//Reference B-GONE, Garbage b collected.
inputObj[prop] = null;
delete inputObj[prop];
}
}
}
};
removeDanglingEvents(dom._leaflet_events, false);
removeDanglingEvents(dom, true);
}
啊三重反引号,明白了,ty。
@FLoibl这是一项非常好的调查:+1:
您能否添加一些日志记录...? https://github.com/Leaflet/Leaflet/blob/5161140e952969c5da27751b79154a2c93f53bfa/src/dom/DomEvent.Pointer.js#L39和https://github.com/Leaflet/Leaflet/blob/fe9e0dom82b30d/fe9e080f230dc/fe9e080b30e3c930d/fe9e080fc93f53bfa/src/dom/DomEvent.Pointer.js#L39 js#L133
当L.Map
被销毁时,这些应该为每个事件运行,并且应该做与您正在做的相同的事情,但我想知道为什么它不能按预期工作。
是的,我知道那个函数,我看到它被调用,但不是所有事件。
我认为问题在于代码将它们作为“pointermove”等附加到 dom,但属性名称是“touchstart”等。此外,“touchstart”一词在属性名称中出现两次,可能是id 和事件名称?
那些“指针”事件是否也应该附加在没有触摸屏的 Windows 10 和 Chrome 中?
不幸的是,我对传单内部工作的了解不够,无法提供真正的解决方案:-(
我知道那个函数,我看到它被调用,但不是所有事件。
现在的问题是:哪些事件没有调用removePointerListener
? 也许我们在这里或那里错过了一个函数调用。
那些“指针”事件是否也应该附加在没有触摸屏的 Windows 10 和 Chrome 中?
是的。 检测系统是否具有触摸屏几乎是不可能的,因此如果浏览器支持指针事件,则假设它们将被使用。
我对传单内部工作的了解不够,无法提供真正的解决方案:-(
嘿,不要绝望,这是一项伟大的调查工作! :微笑:
此错误在 1.0.3 版中不存在。 我拿起@spydmobile jsfiddle 并更改了传单版本,错误消失了http://jsfiddle.net/5hmadjnk/47/ 。 1.1.0 版已经有了。
@benru89这个错误实际上存在于 1.0.3 中,形式为https://github.com/Leaflet/Leaflet/issues/5263 (主要由 https://github.com/Leaflet/Leaflet/pull/ 修复) 5265)。
从 1.0.3 到 1.1.0 的变化也影响了现在已弃用的L.Mixin.Events
和 rollupJS 构建,所以我认为这不能很好地追踪,即使是git bisect
也不行。
@IvanSanchez我比较了 1.0.3 和 1.1.0 中的删除功能,并添加了以下内容:
for (i in this._panes) {
remove(this._panes[i]);
}
this._layers = [];
this._panes = [];
delete this._mapPane;
delete this._renderer;
如果我删除第 6 行,删除 mapPane 的那一行错误就会消失。 但是,我不知道删除它的影响,我想必须删除 mapPane,但是在添加该行时肯定会引入此错误。
@benru89哇,这也是很好的信息:+1:
我现在只是没有看到地图窗格中有什么指针事件处理程序,但是:思考:
我认为通过不删除地图窗格,您只会掩盖问题。 当我跟踪调用堆栈时,问题是一些 _mapPane 对象指向一个被破坏的 dom 元素,因此试图从未定义的位置获取缓存位置。 如果窗格没有被销毁,幽灵事件可能会通过而不会触发异常。
@Floibl我同意你的看法,我认为这不是解决方案,但我在检查调用堆栈时注意到 null _mapPane 所以我尝试不删除该行。 解决方案必须是正确删除我猜的事件处理程序。
我认为导致这种情况的事件处理程序(至少在我的情况下和@spydmobile )被称为“touchExtend”,因此它是一个 Leaflet.draw 处理程序。 我发现删除 Leaflet.draw 的导入也会停止异常。
我找到了另一种解决方法。 使用未记录的选项touchExtend : false
初始化您的地图会停用有问题的处理程序,因此不再有异常。 我真的不知道这样做会丢失哪些功能,但查看代码可能是移动或触摸屏的一些扩展手势? 无论如何,在我的应用程序中,一切似乎都很好。
@IvanSanchez我不确定这是同一个问题,但可能相关。
在进行缩放动画时销毁地图时,您会收到相同的错误: Uncaught TypeError: Cannot read property '_leaflet_pos' of undefined
。
我试图查看代码内部,发现在Map._animateZoom()
里面有一行: setTimeout(Util.bind(this._onZoomTransitionEnd, this), 250);
如果我理解的够多的话,这个timeout在map被移除的时候不会被破坏,所以Map._onZoomTransitionEnd
函数总是被调用。 可能是您的_“此处或那里缺少函数调用”_。
而简化的调用树this._onZoomTransitionEnd
-> this._move
-> this._getNewPixelOrigin
-> this._getMapPanePos
-> getPosition(this._mapPane)
-> return el._leaflet_pos
失败,因为this._mapPane
是 _undefined_。
也许这种情况可以解决,如果您将this._move
和this._moveEnd
调用包装到if (this._mapPane) {}
条件中,但我没有测试它是否有其他一些后果。
替换这个:
_onZoomTransitionEnd: function () {
if (!this._animatingZoom) { return; }
if (this._mapPane) {
removeClass(this._mapPane, 'leaflet-zoom-anim');
}
this._animatingZoom = false;
this._move(this._animateToCenter, this._animateToZoom);
// This anim frame should prevent an obscure iOS webkit tile loading race condition.
requestAnimFrame(function () {
this._moveEnd(true);
}, this);
}
有了这个:
_onZoomTransitionEnd: function () {
if (!this._animatingZoom) { return; }
this._animatingZoom = false;
if (this._mapPane) {
removeClass(this._mapPane, 'leaflet-zoom-anim');
this._move(this._animateToCenter, this._animateToZoom);
// This anim frame should prevent an obscure iOS webkit tile loading race condition.
requestAnimFrame(function () {
this._moveEnd(true);
}, this);
}
}
有任何更新吗? 我遇到了同样的问题。 touchExtend : false
没有帮助。 当我离开拥有地图的视图(此时它正在通过调用 map.remove() 被销毁),然后我导航回该视图时,就会出现问题。 它应该创建并初始化新地图,但我在setMaxBounds
方法中的getPosition
处收到“_leaflet_pos”错误:
Uncaught (in promise) TypeError: Cannot read property '_leaflet_pos' of undefined
at getPosition (webpack-internal:///./node_modules/leaflet/dist/leaflet-src.js:2445)
at NewClass._getMapPanePos (webpack-internal:///./node_modules/leaflet/dist/leaflet-src.js:4409)
at NewClass._moved (webpack-internal:///./node_modules/leaflet/dist/leaflet-src.js:4413)
at NewClass.getCenter (webpack-internal:///./node_modules/leaflet/dist/leaflet-src.js:3774)
at NewClass.panInsideBounds (webpack-internal:///./node_modules/leaflet/dist/leaflet-src.js:3488)
at NewClass._panInsideMaxBounds (webpack-internal:///./node_modules/leaflet/dist/leaflet-src.js:4220)
at NewClass.setMaxBounds (webpack-internal:///./node_modules/leaflet/dist/leaflet-src.js:3444)
还有在setView
方法中:
Uncaught (in promise) TypeError: Cannot read property '_leaflet_pos' of undefined
at getPosition (leaflet-src.js?9eb7:2445)
at NewClass._getMapPanePos (leaflet-src.js?9eb7:4409)
at NewClass.containerPointToLayerPoint (leaflet-src.js?9eb7:3989)
at NewClass._getCenterLayerPoint (leaflet-src.js?9eb7:4446)
at NewClass._getCenterOffset (leaflet-src.js?9eb7:4451)
at NewClass._tryAnimatedPan (leaflet-src.js?9eb7:4526)
at NewClass.setView (leaflet-src.js?9eb7:3181)
map.remove() 之后出现同样的问题,重新初始化我的地图并得到这个确切的错误
与 v1.6.0 相同的问题。 这是一个复杂的问题
这是一个 SSCCE: https ://jsfiddle.net/0oafw694/1/
基本上,运行以下代码......
map = L.map('map');
map.setView(...);
map.setMaxBounds(...);
map.remove();
… 留下两个事件侦听器:
moveend: (1) […]
0: Object { fn: _panInsideMaxBounds(), ctx: undefined } // from setMaxBounds
unload: (2) […]
0: Object { fn: _destroy() , ctx: {…} }
1: Object { fn: _destroyAnimProxy(), ctx: undefined }
zoomanim: (1) […]
0: Object { fn: _createAnimProxy(), ctx: undefined }
我猜, zoomanim/_createAnimProxy
是通过unload/_destroyAnimProxy
,因此没问题。 但是moveend/_panInsideMaxBounds
需要注销。 我会准备PR…
我刚刚为具有动态 id 的地图创建了一个 div,所以当我必须重用 div 时,我 remove() 现有地图以释放内存(即使有一些事件仍在发生),然后用不同的 id 重绘 div,所以我在其中创建了一个新地图。
我还将所有地图存储在一个对象中,因此我可以根据其 id 操作它们(有时我有不止一张地图可见,所有地图都具有动态 ID)
根据我对传单的实验,开发人员操作的任何事件(例如 moveend、movestart 等)都会更改其默认行为并在从 dom 卸载传单时保留在内存中。
我这样做了: @moveend="()=>{enableRecenter = true}"
等等,'moveend' 的处理程序在卸载/移除地图时保留在内存中。
我从地图组件本身删除了这些方法的操作(我自己的实现),现在这个错误不再出现。
所以基本上,永远不要触摸地图方法!!! 当然,除非库检测到此行为并修复此错误。
最有用的评论
@spydmobile在这里,这是我以稍微修改的形式所做的:
我不知道如何在这个该死的评论字段中正确发布代码,抱歉。
我已经编辑了我自己的评论大约 10 次,哈哈