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
Usando http://playground-leaflet.rhcloud.com/ ou qualquer outro site semelhante ao jsfiddle.
Olá e obrigado por relatar esse bug.
No entanto, parece que algo está faltando nas etapas descritas para reproduzir o problema. Eu configurei um exemplo de playground tão bem quanto poderia ao longo das etapas acima: http://playground-leaflet.rhcloud.com/rezop/edit?html , output
Neste exemplo, não consigo ver nenhum erro depois que o mapa é removido, então aparentemente há algo mais acontecendo que causa o problema que você está vendo. Você poderia fornecer mais detalhes, para que possamos rastrear isso.
@perliedman Obrigado pela resposta rápida e pelo exemplo de playground. Eu não sou capaz de reproduzir naquele. Tentando mais alguns cenários. enquanto ainda estou analisando, algumas perguntas -
Preciso remover a camada explícita, se estou fazendo map.remove()
? Meu palpite é que ele cuidará da remoção da camada também, mas você pode confirmar.
E a razão de você ter map.remove () em tempo limite é que, no exemplo, você está destruindo o mapa logo após a criação, caso contrário, não é necessário incluir um tempo limite. correto?
Lembre-se, ao trabalhar com código aberto, _use_ o código-fonte! : wink: Para responder à sua primeira pergunta, _sim_, remove
removerá as camadas: https://github.com/Leaflet/Leaflet/blob/master/src/map/Map.js#L731
A razão de eu colocá-lo em um tempo limite foi para tornar meu exemplo um pouco mais realista, certificando-se de que a camada de blocos foi realmente inicializada corretamente com blocos carregados, etc. O exemplo funciona tão bem sem colocar a chamada remove
um tempo limite, mas parece um pouco um teste artificial.
Olá, acho que também estou tendo esse problema. Este é meu caso de uso básico:
Estou construindo um componente do visualizador (usando o plug - in
Quando o usuário muda a visualização, estou chamando map.remove()
antes de configurar um novo mapa para a nova visualização. O novo mapa é criado no mesmo elemento DOM do antigo (um div com um ID) e não estou modificando o DOM de nenhuma forma fora do Leaflet.
Na visualização inicial, tudo funciona bem. Mas depois de chamar map.remove()
e mostrar uma nova visão, o console reclama: Cannot read property '_leaflet_pos' of undefined
sempre que o mapa é arrastado ou ampliado.
Posso tentar postar um exemplo mínimo em algum momento, mas esse parece ser o mesmo problema. Este erro surge no Chrome, mas não no Firefox.
@egardner sim, por favor, tente criar um exemplo que reproduza isso!
@egardner exatamente o mesmo problema no aplicativo de elétron anteriormente estável (Chromium + Node) que tive que reverter para:
"folheto": "1.0.0",
"leaflet.markercluster": "1.0.0-rc.1.0"
de 1.2.0 para remover o erro Não é possível ler a propriedade '_leaflet_pos' de indefinido
Isso também aconteceu depois de map.remove () antes de recriar o mapa no mesmo elemento DOM. Não tenho tempo agora para criar um exemplo a curto prazo
Também estamos enfrentando um problema semelhante ao tentar destruir um mapa, parece que estamos mantendo referências
Vou reiterar o que disse acima: para que possamos fazer algo sobre esse problema, forneça um exemplo que reproduza o problema.
Oi. Reproduzi esse erro em um violino. em outras palavras, se você criar um mapa dentro de um elemento div, usar o método remove e preencher novamente o mapa no mesmo div, cada movimento do mapa gerará um erro
TypeError não capturado: Não é possível ler a propriedade '_leaflet_pos' de undefined.
Para reproduzir, abra meu violino, clique em remover mapa, clique em mapa de local, abra o console e mova o mapa.
http://jsfiddle.net/spydmobile/5hmadjnk/
Observe, isso só acontece em Chorme, não em FF
Sim, spydmobile, obrigado pelo exemplo, este é o mesmo erro que estou vendo no Chrome conforme relatei acima
Estou vendo o mesmo erro, mas em um caso de uso ligeiramente diferente. O mesmo erro é gerado no redimensionamento devido a uma chamada de 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)
A pilha de chamadas completa começa no manipulador _onResize
. Estou usando react-leaflet
mas nenhuma parte do rastreamento de pilha aponta para isso ou para o código local sendo o problema. Eu tentei algumas versões mais antigas (por exemplo, 1.0.3
e 1.2.0
) pensando que podemos pelo menos ser capazes de bloqueá-lo em uma versão 1.x.x
, mas não tive sorte.
Existe alguma atualização sobre isso? Tendo o mesmo problema aqui integrando folheto em minha aplicação. Depois que o mapa é destruído, o elemento dom ainda tem uma propriedade _leaflet_events, mas livrar-se desse objeto também não ajudou.
Parece que o contexto da função do manipulador está obsoleto (a propriedade privada _mapPane nos argumentos do manipulador aponta para um elemento inexistente).
Estou passando por isso também. Esta é uma das exceções lançadas que estou vendo:
https://sentry.io/share/issue/b414c58ea85c44ee9e0e40ad0781883a/
Parece que isso acontece principalmente quando um usuário usa o botão Voltar do navegador para sair do mapa.
Acho que encontrei a solução:
O div do contêiner do Mapa ainda tem alguns eventos que são disparados mesmo depois de map.off
e map.remove
.
No meu caso, o mapa tem propriedades que começam com _leaflet_
e algumas dessas funções que encontrei no próprio mapa na propriedade "map._leaflet_events".
Esses parecem estar anexados como pointerdown
, pointermove
e outros, mas os nomes das propriedades são como map._leaflet_touchstarttouchstart32
etc.
Descobri que, se iterar e removê-los manualmente (usando removeEventListener
seguida, anulando e excluindo a própria propriedade para uma boa medida), posso reutilizar o div para outro mapa.
Isso também pôs fim aos vazamentos de memória que eu estava observando.
Não posso postar código aqui, mas se você pesquisar a fonte do folheto por POINTER_DOWN
verá os eventos anexados e saberá como destacá-los.
Posso reproduzir isso no Chrome, mas também no FF (em meu próprio projeto e com o jsfiddle fornecido por @spydmobile)
@FLoibl Talvez uma essência, violino ou algum trecho postado em outro lugar para demonstrar a técnica que você está usando com sucesso para contornar o problema?
@spydmobile aqui vai, isso é o que eu fiz de uma forma ligeiramente modificada:
Não tenho ideia de como postar código corretamente neste campo de comentário de merda, sinto muito.
Eu editei meu próprio comentário cerca de 10 vezes agora lol
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);
}
Ah backticks triplos, entendi, ty.
@FLoibl Esta é uma investigação muito boa: +1:
Você poderia adicionar algum registro ao redor ...? https://github.com/Leaflet/Leaflet/blob/5161140e952969c5da27751b79154a2c93f53bfa/src/dom/DomEvent.Pointer.js#L39 e https://github.com/Leaflet/Leaflet/blob/fe9e0f2333888e8c02b9e7f83bf337d91006ed0a/src/dom/DomEvent. js # L133
Eles deveriam estar ocorrendo para todos os eventos quando um L.Map
é destruído e deveriam estar fazendo a mesma coisa que você está fazendo, mas eu me pergunto por que não funciona como esperado.
Sim, conheço essa função e vejo que é chamada, mas não para todos os eventos.
Acho que o problema é que o código os anexa como "pointermove" etc. ao dom, mas os nomes das propriedades são "touchstart" etc. Além disso, a palavra "touchstart" é vista duas vezes no nome da propriedade, talvez um inesperado doube concat do id e nome do evento?
Além disso, esses eventos de "ponteiro" devem ser anexados ao Windows 10 sem touchscreen e no Chrome?
Infelizmente, não sei o suficiente sobre o funcionamento interno dos folhetos para fornecer uma solução real :-(
Eu conheço essa função e vejo que ela é chamada, mas não para todos os eventos.
Agora a pergunta é: Quais são os eventos para os quais removePointerListener
não é chamado? Talvez estejamos perdendo uma chamada de função aqui ou ali.
Além disso, esses eventos de "ponteiro" devem ser anexados ao Windows 10 sem touchscreen e no Chrome?
sim. É quase impossível detectar se um sistema possui uma tela sensível ao toque, portanto, se o navegador suportar eventos de ponteiro, presume- se que eles serão usados.
Eu não sei o suficiente sobre o funcionamento interno dos folhetos para fornecer uma solução real :-(
Ei, não se desespere, este é um ótimo trabalho de investigação! :sorriso:
Este bug não está presente na versão 1.0.3. Peguei @spydmobile jsfiddle e http://jsfiddle.net/5hmadjnk/47/ . Com a versão 1.1.0 já existe.
@ benru89 Este bug estava de fato presente em 1.0.3, na forma de https://github.com/Leaflet/Leaflet/issues/5263 (principalmente corrigido por https://github.com/Leaflet/Leaflet/pull/ 5265).
A mudança de 1.0.3 para 1.1.0 também afetou o agora obsoleto L.Mixin.Events
e a compilação rollupJS, então não acho que isso possa ser rastreado muito bem, nem mesmo com git bisect
.
@IvanSanchez I comparei a função remove em 1.0.3 e 1.1.0 e isto foi adicionado:
for (i in this._panes) {
remove(this._panes[i]);
}
this._layers = [];
this._panes = [];
delete this._mapPane;
delete this._renderer;
Se eu remover a 6ª linha, aquela que exclui o mapPane, o erro desaparece. Não sei, porém, o impacto de remover isso, acho que o mapPane tem que ser excluído, mas esse bug foi certamente introduzido quando essa linha foi adicionada.
@ benru89 Uau, essa também é uma boa informação: +1:
Só não vejo quais manipuladores de eventos de ponteiro existem nos painéis de mapa agora: pensando:
Acho que ao não remover o painel do mapa, você apenas mascara o problema. Quando rastreei a pilha de chamadas, o problema era que algum objeto _mapPane estava apontando para um elemento dom destruído, tentando assim obter uma posição em cache de indefinido. Se o painel não for destruído, os eventos fantasmas podem passar sem acionar a exceção.
@Floibl : concordo com você, acho que não é a solução, mas notei o _mapPane nulo ao verificar a pilha de chamadas, por isso tentei não remover essa linha. A solução tem que ser remover adequadamente os manipuladores de eventos, eu acho.
Acho que o manipulador de eventos que causa isso (pelo menos no meu caso e @spydmobile ) é chamado de "touchExtend", portanto, é um manipulador leaflet.draw. Descobri que remover a importação de leaflet.draw também impede as exceções.
Eu encontrei outra solução alternativa. Inicializar seu mapa com a opção não documentada touchExtend : false
desativa o manipulador problemático, portanto, não há mais exceções. Eu realmente não sei quais recursos estou perdendo ao fazer isso, mas olhando para o código, podem ser alguns gestos estendidos para dispositivos móveis ou telas sensíveis ao toque ?? Em qualquer caso, no meu aplicativo tudo parece funcionar bem.
@IvanSanchez Não tenho certeza se é o mesmo problema, mas pode estar relacionado.
Ao destruir o mapa enquanto a animação de zoom está em andamento, você obtém o mesmo erro: Uncaught TypeError: Cannot read property '_leaflet_pos' of undefined
.
Tentei olhar dentro do código e descobri que dentro de Map._animateZoom()
há uma linha: setTimeout(Util.bind(this._onZoomTransitionEnd, this), 250);
Se bem entendi, esse tempo limite não é destruído quando o mapa é removido, então a função Map._onZoomTransitionEnd
é sempre chamada. Pode ser o seu _ "falta uma chamada de função aqui ou ali" _.
E a árvore de chamadas simplificada this._onZoomTransitionEnd
-> this._move
-> this._getNewPixelOrigin
-> this._getMapPanePos
-> getPosition(this._mapPane)
-> return el._leaflet_pos
falha, porque this._mapPane
é _undefined_.
Talvez esse caso pudesse ser corrigido, se você envolvesse as chamadas this._move
e this._moveEnd
na condição if (this._mapPane) {}
, mas não testei se isso tem outras consequências.
Substitua este:
_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);
}
com isso:
_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);
}
}
Alguma atualização sobre isso? Estou tendo o mesmo problema. touchExtend : false
não ajuda. O problema ocorre quando eu navego para fora da visualização, onde tenho o mapa (ele está sendo destruído neste ponto, chamando map.remove ()) e, em seguida, navego de volta para esta visualização. Ele deve criar e inicializar o novo mapa, mas estou recebendo o erro '_leaflet_pos' em getPosition
no método setMaxBounds
:
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)
E também no método 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)
Mesmo problema após map.remove (), reinicie meu mapa e obtenha este erro exato
Mesmo problema com v1.6.0. É um assunto complicado
Aqui está um SSCCE: https://jsfiddle.net/0oafw694/1/
Basicamente, executando o seguinte código…
map = L.map('map');
map.setView(...);
map.setMaxBounds(...);
map.remove();
… Deixa dois ouvintes de evento anexados:
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 }
Acho que zoomanim/_createAnimProxy
é gerenciado por meio de unload/_destroyAnimProxy
e, portanto, sem problemas. Mas moveend/_panInsideMaxBounds
precisa ser cancelado. Vou preparar um PR ...
Acabei de criar um div para o mapa que tem um id dinâmico, então quando eu tenho que reutilizar o div, eu removo () o mapa existente para liberar memória (mesmo assim, alguns eventos ainda estão acontecendo) e então redesenhar o div com um id diferente para que eu crie um novo mapa nele.
Também armazeno todos os meus mapas em um objeto, para que possa manipulá-los de acordo com seu id (tenho mais de um mapa visível algumas vezes, todos com ids dinâmicos)
De acordo com minhas experiências com o folheto, quaisquer eventos (ex. Moveend, movestart, etc.) que são manipulados pelos desenvolvedores, mudam seu comportamento padrão e permanecem na memória enquanto o folheto é descarregado do dom.
Eu tinha feito isso: @moveend="()=>{enableRecenter = true}"
e assim, o manipulador de 'moveend' permaneceu na memória enquanto descarregava / removia o mapa.
Eu removi as manipulações (minhas próprias implementações) desses métodos do próprio componente do mapa e agora esse erro parou de aparecer.
Então, basicamente, NÃO TOQUE NOS MÉTODOS DE MAPA NUNCA !!! Claro, a menos que a biblioteca detecte esse comportamento e corrija esse bug.
Comentários muito úteis
@spydmobile aqui vai, isso é o que eu fiz de uma forma ligeiramente modificada:
Não tenho ideia de como postar código corretamente neste campo de comentário de merda, sinto muito.
Eu editei meu próprio comentário cerca de 10 vezes agora lol