I'm stuck with my loop. Everything functions, except markerLayer.markers()[i].showTooltip();
Somehow it shows the wrong marker. Am I passing the wrong arguments? Am I missing the logic here?
for (var i = 0; i < features.length; i++) {
var a = side.appendChild(document.createElement('a'));
a.onclick = (function (feature, i) {
return function () {
markerLayer.interaction.hideTooltips()
map.ease.location({
lat: feature.geometry.coordinates[1],
lon: feature.geometry.coordinates[0]
}).zoom(zoomPlaces).optimal(null, null, function (feature) {
markerLayer.markers()[i].showTooltip();
})
}
})
(features[i], i);
}
The latest version of MapBox and Leaflet's JavaScript APIs use map.markerLayer.getLayers() to return array of layer objects that contains a unique _leaflet_id. Please use that id number to help specify the marker you'd like to togglePopup.
Related
I am trying to write a method that creates Leaflet markers with a function updateRoute which should be called whenever the marker is moved.
My problem is that I can't find the index of the marker that was moved as the wayPointIteration variable is evaluated as the total number of markers. I understand why it doesn't work like that now but I don't know how the method can be executed with knowledge of the iteration of that marker.
function updateWayPointsOnMap() {
let options = {draggable: true};
let wayPointIteration = 1;
getRouteWayPoints().forEach(wayPoint => {
let newMarker = L.marker([wayPoint.lat, wayPoint.lng], options).bindTooltip("Way point " + wayPointIteration).addTo(map)
.on('move', function updateRoute(move) {
getRouteWayPoints()[wayPointIteration - 1] = move.latlng;
updateMap();
});
routeMarkers.push(newMarker);
wayPointIteration ++;
});
}
function pushMarkers() {
locInfo = new google.maps.InfoWindow();
for (var i = 0; i < model.length; i++) {
var loc = model[i].places;
var title = model[i].title;
model[i].marker = new google.maps.Marker({
position: loc,
title: title,
animation: google.maps.Animation.DROP,
map: map,
id: i
});
console.log(model[i].title);
model[i].marker.addListener('click', function() {
console.log('InfoWindow Loop entered');
toggleBounce(this);
populateInfoWindow(this, locInfo);
});
wikiLink(model[i].marker);
}
}
Its not advisable to make functions within a loop so i want to make the function outside the loop.
You shouldn't make multiple functions at all, in point of fact, you should just make it once and then attach it as a listener in the loop. such that:
function handleMarkerClick() {
console.log('InfoWindow Loop entered');
toggleBounce(this);
populateInfoWindow(this, locInfo);
}
model[i].marker.addEventListener('click', handleMarkerClick);
Note that if you need to pass contextual information to that handleMarkerClick function that's only available inside the loop, let's say you want it to know the index (i) for some reason, you can bind it:
model[i].marker.addEventListener('click', handleMarkerClick.bind(model[i].marker, i);
and then the signature of the function would need to be function handleMarkerClick(i, event){}
Everytime I run this loop, each marker in the markers array has it's icon overwritten by the results from let icon = iconLoader.getIcon(data[index][5]);.
leaving each marker having the last loaded icon, instead of the icon loading during each pass of the for loop.
I thought that passing icon into the closure would essentially pass it by value, preventing it from being overwritten outside the scope of the closure, but this doesn't seem to be working for me. What am I missing?
var markers = []
for (var index in data) {
let icon = iconLoader.getIcon(data[index][5]);
var createMarker = (function (i) {
return function () {
var marker = new L.marker([data[index][2], data[index][3]])
.setIcon(i)
markers.push(marker);
}
})(icon);
createMarker();
}
var iconLoader = (function () {
var icon = L.icon({
// options
});
return {
getIcon: function (iconName) {
// do stuff to get icon
return icon;
}
};
}());
JSFiddle
So, as I mentioned in my original comment, JavaScript objects and arrays are always passed by reference unless you explicitly create and pass a copy. Nothing in your code is inherently wrong and is not causing this issue - it is actually an issue with how leaflet is handling the object references internally. The way to avoid this is to do a deep copy on the result from iconLoader.getIcon(). If you are using jQuery, you can do it very simply by using $.extend().
for (var index in data) {
let icon = $.extend(true, {}, iconLoader.getIcon(data[index][2]));
var marker = new L.marker([data[index][0], data[index][1]])
.setIcon(icon);
markers.push(marker);
}
If not, you can look into non-jQuery solutions - it's not ideal, but they're everywhere.
I was typing this up as mhodges was writing his answer. I'll go ahead and post it as it's a different solution that also solved the issue for me.
After looking at mhodges' working demo, I noticed he had set up the iconloader a bit differently than I had. He had the iconloader as an object...
var iconLoader = {
getIcon: function (elem) {
return elem;
}
}
while mine was set up as a closure...
var iconLoader = (function () {
var icon = L.icon({
// options
});
return {
getIcon: function (iconName) {
// do stuff to get icon
return icon;
}
};
}());
I thought that maybe I could try setting it up a bit differently and see if that made a difference, and VOILA!
const proto = {
getIcon (iconName) {
var icon = L.icon({
// options
});
// do stuff to get icon
return icon;
}
};
function factoryIcon() {
return Object.create(proto);
}
and then grabbed the icon with
const iconFactory = factoryIcon();
let icon = iconFactory.getIcon(data[index][5]);
I want to implement a functionality on Gmap API, that if the zoom is more than 17, show all the markers(otherwise, just hide them). However, when I write code like the following, it just does not work.
PS: the code might not be exactly correct in syntax and arrangement, but it express what I mean
// #latlong is an array of tuple (latitude , longtitude)
// #myMap is the google map object passed to the function
function placeMarker( myMap , latlon)
{
for(var i = 0 ; i < latlon.length ; i ++)
{
myMarker = new google.maps.Marker( {
position: new google.maps.LatLng(latlon[i][0], latlon[i][1])
});
google.maps.event.addListener(myMap, 'zoom_changed', function() {
zoomLevel = myMap.getZoom()
if(zoomLevel >= 17)
{
myMarker.setMap(myMap)
}
else
{
myMarker.setMap(null)
}
});
}
}
And I just change my code to:
function placeMarker( myMap , latlon)
{
for(var i = 0 ; i < latlon.length ; i ++)
{
myMarker = new google.maps.Marker( {
position: new google.maps.LatLng(latlon[i][0], latlon[i][1])
});
(function(myMarker_copy){
google.maps.event.addListener(myMap, 'zoom_changed', function() {
zoomLevel = myMap.getZoom()
if(zoomLevel >= 17)
{
myMarker_copy.setMap(myMap)
}
else
{
myMarker_copy.setMap(null)
}
});
}(myMarker));
}
}
And the second version works.
I know how to make it work, however, I really do not know why it work and why the other does not. Maybe this has something to do with the function closure or parameter passing principle of JS(I have checked a lot references, but some of them just hold different ideas). Could anybody give me a help?
really appreciate it.
The first example which doesn't work: There is one global variable myMarker and many event listeners which at the end all operate on one marker and that is the one created the last in the loop.
The second example which does work: because of closure each event listener receive its own local copy of marker variable. So, when event listener is called it has correct marker value.
I've read similar posts, but still didn't find a solution for myself. Basically I have an array with countries+towns in PHP and I need to show them on the map with markers. Here is my code:
function showAddress(markers) {
var address = "<?php echo $Fcity[$j], " , ", $Fcountry[$j]?>";
if (geocoder) {
geocoder.getLatLng(address, function(point) {
if (!point) {
alert(address + " not found");
} else {
var marker = new GMarker(point);
map.addOverlay(marker);
markers[i] = marker;
marker.openInfoWindowHtml(address);
}
}
);
}
}
Everything seems to work if I geocode one location, but I can't put it into a loop to process all of them.
for (var i = 0; i < markers.length; i++) {
showAddress(markers[i]);
}
In your showAddress function, you reference markers[i].
However, you don't pass in i... that variable is not in the scope of the function. So, you aren't iterating and adding, you are adding variables over and over to a non-existent place in the array.
You either need to pass in i or not encapsulate showAddress in a function.
How about making the function showAddresses and putting the loop in the function.