Leaflet popup.update() Resizing Solution - Recreating a Popup Everytime, Unable to Click Embedded URL

人盡茶涼 提交于 2019-12-06 06:52:16

Welcome to SO!

Hum indeed it looks like the given workaround does create an infinite loop when you specify the popup content as HTML string containing an <img>. What happens is that when an image completes loading, the popup.update() resets the Popup content using the HTML string, hence re-creates the <img> element, which emits a new "load" event, even if now it comes from browser cache. Then the listener executes popup.update() again, etc.

Demo (open your Web Console to see the inifinite loop logging "got load event from IMG"):

var map = L.map('map').setView([48.86, 2.35], 11);

// Modify the cache busting value to force browser fetching from network.
var imgSrc = 'https://a.tile.openstreetmap.org/0/0/0.png?bust=1';
var popupContent =
  '<a href="https://a.tile.openstreetmap.org/0/0/0.png" target="_blank">' +
  '<img src="' + imgSrc + '"/></a>';

L.marker([48.86, 2.35]).addTo(map).bindPopup(popupContent);

document.querySelector(".leaflet-popup-pane").addEventListener("load", function(event) {
  var tagName = event.target.tagName,
    popup = map._popup;
  console.log("got load event from " + tagName);
  if (tagName === "IMG" && popup) {
    popup.update();
  }
}, true);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
html,
body,
#map {
  height: 100%;
  margin: 0;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.3/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.3.3/dist/leaflet-src.js" integrity="sha512-GosS1/T5Q7ZMS2cvsPm9klzqijS+dUz8zgSLnyP1iMRu6q360mvgZ4d1DmMRP2TDEEyCT6C16aB7Vj1yhGT1LA==" crossorigin=""></script>

<div id="map"></div>

In your very case, if you are sure there will be only 1 Popup open at any given time, and that it includes only a single <img>, you could simply set a flag at first "load" event on that Popup, in order to prevent the inifinite looping:

var map = L.map('map').setView([48.86, 2.35], 11);

// Modify the cache busting value to force browser fetching from network.
var imgSrc = 'https://a.tile.openstreetmap.org/0/0/0.png?bust=2';
var popupContent =
  '<a href="https://a.tile.openstreetmap.org/0/0/0.png" target="_blank">' +
  '<img src="' + imgSrc + '"/></a>';

L.marker([48.86, 2.35]).addTo(map).bindPopup(popupContent);

document.querySelector(".leaflet-popup-pane").addEventListener("load", function(event) {
  var tagName = event.target.tagName,
    popup = map._popup;
  console.log("got load event from " + tagName);
  // Also check if flag is already set.
  if (tagName === "IMG" && popup && !popup._updated) {
    popup._updated = true; // Set flag to prevent looping.
    popup.update();
  }
}, true);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
html,
body,
#map {
  height: 100%;
  margin: 0;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.3/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.3.3/dist/leaflet-src.js" integrity="sha512-GosS1/T5Q7ZMS2cvsPm9klzqijS+dUz8zgSLnyP1iMRu6q360mvgZ4d1DmMRP2TDEEyCT6C16aB7Vj1yhGT1LA==" crossorigin=""></script>

<div id="map"></div>

(note that SO code snippets seem to prevent <a href> links to open, so here is a Plunk to check that the link does open normally: https://next.plnkr.co/edit/ore09Yxmm6DVmJGc)

Now to generalize the solution for the case where an arbitrary number of images are contained in the HTML string, and when multiple Popups can be open simultaneously, we could imagine:

  1. On "popupopen" and each image "load" events, check if all Popup images have a non zero naturalWidth, update otherwise.
  2. Store the reference to the Popup on each image, so that we do not have to resort to read the map._popup (which reference the last open Popup only).

var map = L.map('map', {
  closePopupOnClick: false
}).setView([48.86, 2.35], 11);

// Modify the cache busting value to force browser fetching from network.
var imgSrc1 = 'https://a.tile.openstreetmap.org/0/0/0.png?bust=3';
var imgSrc2 = 'https://a.tile.openstreetmap.org/11/1037/704.png?bust=3';
var popupContent =
  '<a href="' + imgSrc1 + '" target="_blank">' +
  '<img src="' + imgSrc1 + '"/></a>' +
  '<a href="' + imgSrc2 + '" target="_blank">' +
  '<img src="' + imgSrc2 + '"/></a>';

L.marker([48.86, 2.35]).addTo(map).bindPopup(popupContent, {
  autoClose: false
}).on('click', function() {
  // Open another Popup after this one.
  m2.openPopup();
});

var m2 = L.marker([48.86, 2.32]).bindPopup('Second Popup', {
  autoClose: false
}).addTo(map);

// Prepare the Popup when it opens.
map.on('popupopen', function(event) {
  var popup = event.popup;
  popup._imgAllSized = popupImgAllSized(popup);
});

document.querySelector(".leaflet-popup-pane").addEventListener("load", function(event) {
  var target = event.target,
    tagName = target.tagName,
    popup = target._popup;
  console.log("got load event from " + tagName);
  // Also check the Popup "_imgAllSized" flag.
  if (tagName === "IMG" && popup && !popup._imgAllSized) {
    console.log('updated');
    // Update the flag, in case all images have finished loading.
    popup.update();
    popup._imgAllSized = popupImgAllSized(popup);
  }
}, true);

function popupImgAllSized(popup) {
  // Get the HTMLElement holding the Popup content.
  var container = popup._contentNode;
  var imgs = container.querySelectorAll('img');
  var imgAllSized = true;
  for (var i = 0; i < imgs.length; i += 1) {
    // Store reference to popup in <img>
    imgs[i]._popup = popup;
    // Check if the image has unknown size.
    if (!imgs[i].naturalWidth) {
      imgAllSized = false;
    }
  }
  console.log(imgAllSized);
  return imgAllSized;
}

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
html,
body,
#map {
  height: 100%;
  margin: 0;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.3/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.3.3/dist/leaflet-src.js" integrity="sha512-GosS1/T5Q7ZMS2cvsPm9klzqijS+dUz8zgSLnyP1iMRu6q360mvgZ4d1DmMRP2TDEEyCT6C16aB7Vj1yhGT1LA==" crossorigin=""></script>

<div id="map"></div>

Then we could even further improve this solution by trying to update as soon as the images have their naturalWidth, instead of waiting for their "load" event, so that even while the browser is still fetching them, the Popup size and position is updated.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!