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://playground-leaflet.rhcloud.com/rezop/edit?html , output
В этом примере я не вижу никаких ошибок после удаления карты, поэтому очевидно, что происходит что-то еще, что вызывает проблему, которую вы видите. Не могли бы вы предоставить более подробную информацию, чтобы мы могли это отследить.
@perliedman Спасибо за быстрый ответ и за создание примера игровой площадки. Я не могу воспроизвести на этом. Пробуем еще несколько сценариев. пока я все еще изучаю это, пара вопросов -
Мне нужно явное удаление слоя, если я делаю map.remove()
? Я предполагаю, что он также позаботится об удалении слоя, но вы можете подтвердить.
Причина, по которой у вас есть map.remove () при тайм-ауте, заключается в том, что в этом примере вы уничтожаете карту сразу после создания, иначе это не требуется заключать в тайм-аут. верный?
Помните, что при работе с открытым исходным кодом _используйте_ исходный код! : wink: Чтобы ответить на ваш первый вопрос, _yes_, remove
удалит слои: https://github.com/Leaflet/Leaflet/blob/master/src/map/Map.js#L731
Причина, по которой я поместил его в тайм-аут, заключалась в том, чтобы сделать мой пример немного более реалистичным, убедившись, что слой плитки действительно правильно инициализирован с загруженными плитками и т. Д. Пример работает так же хорошо, не помещая вызов remove
в тайм-аут, но это похоже на искусственный тест.
Привет! Думаю, я тоже столкнулся с этой проблемой. Вот мой основной вариант использования:
Я создаю компонент просмотра (используя плагин Leaflet-IIIF , но я не думаю, что это на что-то влияет) для объектов с несколькими страницами / поверхностями, а не для отображения реальной карты. Когда средство просмотра загружается, появляется серия эскизов, которые пользователь может щелкнуть, чтобы обновить, какой вид объекта отображается в центральной области пользовательского интерфейса.
Когда пользователь меняет представление, я вызываю map.remove()
перед настройкой новой карты для нового представления. Новая карта создается на том же элементе DOM, что и старая (div с идентификатором), и я никоим образом не изменяю DOM за пределами Leaflet.
На первый взгляд все работает нормально. Но после вызова map.remove()
и отображения нового представления консоль жалуется: Cannot read property '_leaflet_pos' of undefined
всякий раз, когда карта перетаскивается или масштабируется.
В какой-то момент я могу попытаться опубликовать минимальный пример, но, похоже, это та же проблема. Эта ошибка возникает в Chrome, но не в Firefox.
@egardner: да, попробуйте создать пример, воспроизводящий это!
@egardner точно
"листовка": "1.0.0",
"leaflet.markercluster": "1.0.0-rc.1.0"
из 1.2.0 для удаления ошибки Невозможно прочитать свойство _leaflet_pos из undefined
Это также было после map.remove () перед воссозданием карты в том же элементе DOM. У меня сейчас нет времени создавать и примерять в краткосрочной перспективе
Мы также сталкиваемся с аналогичной проблемой, пытаясь уничтожить карту, похоже, она цепляется за ссылки.
Я повторю то, что я сказал выше: чтобы мы могли что-то сделать с этой проблемой, предоставьте пример, воспроизводящий проблему.
Привет. Я воспроизвел эту ошибку на скрипке. Проще говоря, если вы создаете карту внутри элемента div, затем используете метод remove, а затем повторно заполняете карту в том же div, каждое перемещение карты будет генерировать ошибку
Uncaught TypeError: невозможно прочитать свойство _leaflet_pos, равное undefined.
Чтобы воспроизвести, откройте мою скрипку, щелкните удалить карту, щелкните карту места, затем откройте консоль и переместите карту.
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, но избавление от этого объекта тоже не помогло.
Кажется, что контекст функции-обработчика устарел (частное свойство _mapPane в аргументах обработчика указывает на несуществующий элемент).
Я тоже это переживаю. Это одно из возникающих исключений, которые я вижу:
https://sentry.io/share/issue/b414c58ea85c44ee9e0e40ad0781883a/
Похоже, что это чаще всего происходит, когда пользователь использует кнопку браузера «Назад», чтобы покинуть карту.
Думаю, я нашел решение:
В div контейнера Map все еще есть некоторые события, которые запускаются даже после map.off
и map.remove
.
В моем случае карта имеет свойства, которые начинаются с _leaflet_
и некоторые из этих функций, которые я обнаружил на самой карте в свойстве "map._leaflet_events".
Кажется, что они прикреплены как pointerdown
, pointermove
и т. Д., Но имена свойств похожи на map._leaflet_touchstarttouchstart32
и т. Д.
Я обнаружил, что если я повторяю их и удаляю вручную (используя removeEventListener
затем обнуляя и удаляя само свойство для хорошей меры), я могу повторно использовать div для другой карты.
Это также положило конец наблюдаемым мною утечкам памяти.
Я не могу разместить здесь код, но если вы выполните поиск в источнике листовок по запросу POINTER_DOWN
вы увидите прикрепляемые события и узнаете, как их отсоединить.
Я могу воспроизвести это в Chrome, но также и в FF (как в моем собственном проекте, так и с помощью jsfiddle, предоставленного @spydmobile)
@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);
}
А, тройные обратные кавычки, понял, ты.
@FLoibl Это очень хорошее расследование: +1:
Не могли бы вы добавить логирование ...? https://github.com/Leaflet/Leaflet/blob/5161140e952969c5da27751b79154a2c93f53bfa/src/dom/DomEvent.Pointer.js#L39 и https://github.com/Leaflet/Leaflet/blob/fe9e0f2333888e8c02b9e7f83bf337d91006ed0a/src/dom/DomEvent. 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:
Я просто не вижу, какие обработчики событий указателя сейчас есть на панелях карты: Think:
Я думаю, что, не удаляя панель карты, вы только замаскируете проблему. Когда я отслеживал стек вызовов, проблема заключалась в том, что какой-то объект _mapPane указывал на разрушенный элемент dom, таким образом пытаясь получить кешированную позицию из undefined. Если панель не уничтожена, призрачные события могут пройти, не вызывая исключения.
@Floibl Я согласен с вами, я думаю, что это не решение, но я заметил нулевой _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);
Насколько я понимаю, этот тайм-аут не уничтожается при удалении карты, поэтому всегда вызывается функция Map._onZoomTransitionEnd
. Это может быть ваш _ "пропущенный вызов функции здесь или там" _.
И упрощенное дерево вызовов this._onZoomTransitionEnd
-> this._move
-> this._getNewPixelOrigin
-> this._getMapPanePos
-> getPosition(this._mapPane)
-> return el._leaflet_pos
не работает, потому что this._mapPane
_неопределено_.
Возможно, этот случай можно исправить, если вы включите вызовы 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 ()), а затем я возвращаюсь к этому представлению. Он должен создать и инициализировать новую карту, но я получаю ошибку '_leaflet_pos' в getPosition
в методе 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)
А также в методе 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…
Я только что создал div для карты с динамическим идентификатором, поэтому, когда мне нужно повторно использовать div, я удаляю () существующую карту, чтобы освободить память (даже если некоторые события все еще происходят вокруг), а затем перерисуйте div с другим идентификатором, чтобы создать в нем новую карту.
Я также храню все свои карты в объекте, поэтому я могу управлять ими в соответствии с его идентификатором (у меня несколько раз отображается несколько карт, все с динамическими идентификаторами)
Согласно моим экспериментам с листовкой, любые события (например, moveend, movestart и т. Д.), Которыми манипулируют разработчики, изменяют свое поведение по умолчанию и остаются в памяти при выгрузке листовки из dom.
Я сделал это: @moveend="()=>{enableRecenter = true}"
и поэтому обработчик 'moveend' остался в памяти при выгрузке / удалении карты.
Я удалил манипуляции (мои собственные реализации) этих методов из самого компонента карты, и теперь эта ошибка перестала появляться.
В общем, НИКОГДА НЕ ПРИКАСАЙТЕСЬ К МЕТОДАМ КАРТЫ !!! Конечно, если только библиотека не обнаружит такое поведение и не исправит эту ошибку.
Самый полезный комментарий
Вот и @spydmobile, вот что я сделал в слегка измененной форме:
Я понятия не имею, как правильно разместить код в этом гребаном поле комментария, извините за это.
Я уже 10 раз редактировал свой комментарий, лол