I am trying to draw country shapes on a leaflet map using L.GeoJSON(data).addTo(map). I then want to bind a popup to the click event of that country shape...
new L.GeoJSON(data, {
onEachFeature: function(feature, layer) {
layer['on']('click', popupFunction);
}
}).addTo(this.map);
popupFunction = function(event) {
var layer = event.target;
// Open the 'add' popup and get our content node
var bound = layer.bindPopup(
"<div>Hello World!</div>"
).openPopup();
// Ugly hack to get the HTML content node for the popup
// because I need to do things with it
var contentNode = $(bound['_popup']['_contentNode']);
}
Now this works fine when the data is a single polygon, because then the layer attribute passed to the onEachFeature function is just that: a layer.
However if the data is a multipolygon (i.e. the US) this stops working because the "layer" is now a layerGroup (it has a _layers) attribute and therefore has no _popup attribute and so I can't get the _contentNode for the popup.
It seems like this should be quite a common thing, wanting a popup on a layerGroup. Why does it have no _popup attribute?
short answer: layergroup does not support popup
plan B:
you should consider using FeatureGroup, it extends LayerGroup and has the bindPopup method and this is an example
L.featureGroup([marker1, marker2, polyline])
.bindPopup('Hello world!')
.on('click', function() { alert('Clicked on a group!'); })
.addTo(map);
You cannot bind a L.Popup to anything else than a L.Layer because the popup will some coordinates to anchor on.
For a L.Marker it will be the position (L.Latlng), for the L.Polygon it will be the center (look at the code to see how it is calculated).
As for the other cases (like yours), you can open a popup but you will have to decide where the popup opens:
var popup = L.popup()
.setLatLng(latlng)
.setContent('<p>Hello world!<br />This is a nice popup.</p>')
.openOn(map);
First of all, you should be able to bind popUp in similar way to Using GeoJSON with Leaflet tutorial. Something like this:
var geoJsonLayer = L.geoJson(data, {
onEachFeature: function(feature, layer) {
layer.bindPopup('<div>Hello World!</div>');
}
}).addTo(map);
How to further process your popUps depends on your usecase. Maybe this can be enough for you:
geoJsonLayer.eachLayer(function(layer) {
var popUp = layer._popup;
// process popUp, maybe with popUp.setContent("something");
});
Hope this helps..
I used this code to open all popups in a layer group:
markers.eachLayer(marker => marker.openPopup());
While dealing with layer groups, you might want to consider passing { autoClose: false, closeOnClick: false } options while binding popup, so popups won't get closed while opening new popup, or if user clicks on the map:
marker.bindPopup(item.name, { autoClose: false, closeOnClick: false });
Related
I am using leafletjs to build a web map and trying to figure out how to show a modal window when a marker is clicked (instead of the default popup method).
Here's my setup:
var myAirports = L.geoJson(myData, {
pointToLayer: function(latlng){
..snip..
},
onEachFeature: function(feature,layer){
$('#myModalOne').modal(options);
}
});
myAirports.addTo(map);
My HTML is like so:
<div id="myModalOne">....</div>
<div id="myModalTwo">....</div>
Lets say my data has a featurecollection with a key of 'name' (i.e., 'name': 'Bush Airport') for each feature. Would I just add a switch statement to my onEachFeature function?
Just need a little guidance,thanks.
Note: I am using Bootstrap for the modal windows
If I understand you correctly, you don't need to set the pointToLayer option which is useful if you want to display something else than a marker.
What you need is to catch the click event on the markers and display a modal window. There is no popup by default.
var myAirports = L.geoJson(myData, {
onEachFeature: function(feature,layer){
layer.on('click', function(e){
$('#myModal'+feature.properties.name).modal(options);
// or whatever that opens the right modal window
});
}
});
myAirports.addTo(map);
So, I know that we have Marker.togglePopup() in Mapbox GL API.
But can we close all popups programmatically?
Here is an example: https://jsfiddle.net/kmandov/eozdazdr/
Click the buttons at the top right to open/close the popup.
Given you have a popup and a marker:
var popup = new mapboxgl.Popup({offset:[0, -30]})
.setText('Construction on the Washington Monument began in 1848.');
new mapboxgl.Marker(el, {offset:[-25, -25]})
.setLngLat(monument)
.setPopup(popup)
.addTo(map);
You can close the popup by calling:
popup.remove();
or you can open it by calling:
popup.addTo(map);
As you can see in the Marker source, togglePopup uses these two methods internally:
togglePopup() {
var popup = this._popup;
if (!popup) return;
else if (popup.isOpen()) popup.remove();
else popup.addTo(this._map);
}
The accepted answer didn't apply to my use case (I wasn't using a Marker). I was able to come up with a different solution by utilizing the mapbox's built-in event workflow. Hopefully this helps someone else.
Mapbox allows you to listen to events on the map (and manually trigger them). The documentation doesn't mention it, but you can use custom events.
Given you have a popup:
// Create popup and add it to the map
const popup = new mapboxgl.Popup({ offset: 37, anchor: 'bottom' }).setDOMContent('<h5>Hello</h5>').setLngLat(feature.geometry.coordinates).addTo(map);
// Add a custom event listener to the map
map.on('closeAllPopups', () => {
popup.remove();
});
When you want to close all popups, fire the event:
map.fire('closeAllPopups');
Mapbox automatically uses the class .mapboxgl-popup for the popup. You can also add additional classes with options.className.
So, if you have jQuery available, just do:
$('.mapboxgl-popup').remove();
Or plain javascript:
const popup = document.getElementsByClassName('mapboxgl-popup');
if ( popup.length ) {
popup[0].remove();
}
I'm pretty sure you can assume there's only ever one popup open. The default behavior seems to be that if one is open and a second item is clicked, the first popup is removed from the DOM completely when the second one opens. If your application allows multiple open popups somehow, you will need to loop through and remove each with plain js, instead of using the first item only.
I know it's an old question but I recently worked on Mapbox. As of mapbox-gl v2.3, mapboxgl.Popup has a closeOnClick property where if set to true, the popup disappears when you click away from the popup.
let popup = new mapboxgl.Popup({
anchor: "top",
closeOnClick: true,
});
map.on(
"click",
"location",
(e) => {
map.getCanvas().style.cursor = "pointer";
let coordinates = e.features[0].geometry.coordinates.slice();
popup
.setLngLat(coordinates)
.setHTML("what I want to display")
.addTo(map);
}
);
Alternatively, you can show the popup on "mouseenter" instead of on "click" and add a "mouseleave" event to remove the popup:
map.on(
"mouseleave",
"location",
() => {
map.getCanvas().style.cursor = "";
popup.remove();
}
);
wrap your popup with React.StrictMode and closeOnClick={false}.
const [popup, setPopup] = useState<boolean>(false);
...
{popup && (
<React.StrictMode>
<Popup longitude={52.001126260374704} latitude={34.906269171550214}
anchor="bottom"
onClose={() => {
setPopup(false)
}}
closeOnClick={false}
>
You are here
</Popup>
</React.StrictMode>
)}
Inside a jquery $.getJSON call, I create several markers and popups, then bind the popups to the markers:
$.each(points, function(index, p) {
var marker = L.marker(L.latLng(p.lat, p.lon), { title: p.text });
var popup = L.popup().setContent(p.text);
marker.bindPopup(popup);
}
I want all popups on the markers to be open after the map has loaded (and stay open, if possible). The Leaflet 1.0 documentation states: " Use Map.openPopup ... or use Map.addLayer to open as many as you want." (see leaflet - popup).
If possible, I would prefer using a "built in" solution to one where I have to fiddle around (like in SO - keep popups open). One advantage is that that approach can be generically used, for example to open all tooltips as well. And it would probably help me understand layers better.
I have tried several approaches, with LayerGroups and FeatureGroups, to no avail. How would you open a group of popups at once?
You can use a layerGroup containing all your popups. That way you have all the functionalities of group control for your popups.
I reproduced a basic example on JSfidle (https://jsfiddle.net/vaillant/f4zpr6us/) but you can imagine removing only certain popups from the layerGroup instead of removing all the popups at once. Below is the crucial part of the code:
var popup_layer = new L.layerGroup();
$.each(testData, function(index, p) {
var marker = L.marker(L.latLng(p.lat, p.lon));
marker.addTo(map);
popup = new L.popup({offset: new L.Point(0, -30)});
popup.setLatLng(L.latLng(p.lat, p.lon));
popup.setContent(p.text);
popup.openPopup();
popup_layer.addLayer(popup);
});
popup_layer.addTo(map);
I would like to use a custom icon for the single value markers of the leaflet marker clustering extension. Changing the default icon of leaflet with the following code snippet doesn't work:
var newIcon = L.Icon.Default.extend({
options: {
iconUrl: 'new_icon_location.png'
}
});
Marker clustering extension: https://github.com/Leaflet/Leaflet.markercluster
As for creating an icon in Leaflet, the syntax would be:
var newIcon = L.icon({
iconUrl: 'new_icon_location.png'
});
If you want all markers to use that icon instead of the default one (i.e. L.Icon.Default as you figured out), you should rather override the icon option of L.Marker class (before you start instantiating markers, of course):
L.Marker.mergeOptions({
icon: newIcon
});
As for changing the default icons used by Leaflet.markercluster plugin, you should use option iconCreateFunction:
var mcg = L.markerClusterGroup({
iconCreateFunction: function (cluster) {
// create an icon, possibly based on cluster properties.
return clusterIcon;
}
});
Now I am not exactly sure of what you intend to do when you say "use a custom icon for the single value markers of the leaflet marker clustering". I guess you use option singleMarkerMode and you would like a specific icon to be applied to "clusters of size 1" (which are in fact normal markers which icon is overriden by Marker Cluster plugin)?
In that case, a simple solution would have been not to use the singleMarkerMode option, and instead to make all your individual markers use that specific icon. Possibly through replacing the markers' default icon indeed.
But if for whatever reason you still want to use the singleMarkerMode option, then it is not that complicated either. You could do for instance:
var mcg = L.markerClusterGroup({
singleMarkerMode: true,
iconCreateFunction: function (cluster) {
var childCount = cluster.getChildCount();
return childCount === 1 ? iconForSize1 : someOtherIcon;
}
});
My web application uses the following popup-overlay plugin to show profile content in a popup on the screen:
http://vast-eng.github.io/jquery-popup-overlay/
My problem is related to a Google Map - marker infowindow that I use within the popup window.
When I try to close the infowindow, the popup disappears as well.
This is wrong! I don't immediately see why it is doing this.
If I don't find a solution, I could disable or hide the X, but I prefer not to do this.
The "where" section in the following link shows you the problem:
http://www.zwoop.be/dev/#list/bars/1
Edit
The event parameter returns "undefined" for the following event listener:
google.maps.event.addListener(self.marker.infowindow, "closeclick", function(e)
{
console.log(e);
});
Thanks
Edit2
Here is a quick fiddle that illustrates the problem:
http://jsfiddle.net/462HF/
The blur function in the popup overlay plugin is getting called when you click the 'X' in the infoWindow on the google map, that is why the entire popup is closing. Below is the code in the jquery.popupoverlay.js file:
if (options.blur) {
blurhandler = function (e) {
if (!$(e.target).parents().andSelf().is('#' + el.id)) {
methods.hide(el);
}
};
}
The 'X' is an image, and its parents()only go back to the InfoWindow div, but not the popup div. Therefore, it will call methods.hide(el) and close the popup.
There is probably a better way of determining if it was clicked, but changing it to the following works. It just checks the target's img source:
if (options.blur) {
blurhandler = function (e) {
if (!$(e.target).parents().andSelf().is('#' + el.id) ) {
if(!($(e.target).attr('src') === 'http://maps.gstatic.com/mapfiles/api-3/images/mapcnt3.png'))
methods.hide(el);
}
};
}