Key in for loop has an unexpected value - javascript

I have a simple google map with multiple markers (4 in this case).
I want to add a "bubble" on click event. Markers are rendered fine, but the bubble (infowindow) show always on the last pin. I mean:
I click marker[1] - infowindow shows up on marker[3]
I click marker[[2] - infowindow shows up on marker[3]
etc.
I think that the problem is in the way I loop my array
Here is my loop, that iterates through 4 elements of array:
var key = 0;
var markers = new Array();
var infowindows = new Array();
for(key in myJson.hotels)
{
var newLatlng = new google.maps.LatLng(myJson.hotels[key].latitude,myJson.hotels[key].longitude);
markers[key] = new google.maps.Marker(
{
position: newLatlng,
map: map,
title: 'Hello World!'
});
// the code above works fine - it renders 4 pins o my map
infowindows[key] = new google.maps.InfoWindow(
{
content: contentString
});
google.maps.event.addListener(markers[key], 'click', function() {
//console.log(key); <-- this always return [3]
infowindows[key].open(map,markers[key]);
});
//console.log(key); <-- this always return the right key - 0,1,2,3
}
}

The function in addListener gets called asynchronously. When it gets called you dont know which value key has.
You can come arround this by storing the key in a closure.
google.maps.event.addListener(markers[key], 'click',
function (k) {
return function() { infowindows[k].open(map,markers[k]);
}(key)
});

I used a #phylax hint and I solved the problem this way:
I made a new function:
function addClickEventToMarker(aMap,aKey){
google.maps.event.addListener(markers[aKey], 'click', function() {
//console.log(key); <-- this always return [3]
infowindows[aKey].open(aMap,markers[aKey]);
});
}
and I call the function in my 'for' loop:
for(key in myJson.hotels)
{
var newLatlng = new google.maps.LatLng(myJson.hotels[key].latitude,myJson.hotels[key].longitude);
markers[key] = new google.maps.Marker(
{
position: newLatlng,
map: map,
title: 'Hello World!'
});
infowindows[key] = new google.maps.InfoWindow(
{
content: contentString
});
addClickEventToMarker(map, key);
}
}

Related

how to remove map markers on setInterval refresh (js)

Guys need a bit of help with google maps, I'm creating a tracking web app using MySQL DB coordinates, the tracking works well so far except the google map markers keep repeating when refreshed, I have set the map marker refresh using a setInterval function for every 5 seconds(for testing). I've tried clearoverlays() methods and remove map markers method(from google sample) but doesn't work. Appreciate your help, thank you
<script defer
src="https://maps.googleapis.com/maps/api/js?key="KEY"8&callback=initMap">
</script>
<script type="text/javascript">
setInterval(function () {
BindMarker();
}, 5000);
var customIcons = {
blue: { icon: 'blue48.png'},
};
var marker;
var map = null;
var infoWindow = null;
function load() {
map = new google.maps.Map(document.getElementById("map"), {
center: new google.maps.LatLng(1.4370993, 110.3387572),
zoom:15,
});
infoWindow = new google.maps.InfoWindow;
}
function BindMarker() {
downloadUrl('maps1.php', function (data) {
var xml = data.responseXML;
var markers = xml.documentElement.getElementsByTagName("marker");
for (var i = 0; i < markers.length; i++) {
var point = new google.maps.LatLng(
parseFloat(markers[i].getAttribute("lat")),
parseFloat(markers[i].getAttribute("lng")));
var icon = customIcons["blue"] || {};
marker = new google.maps.Marker({
map: map,
animation: google.maps.Animation.BOUNCE,
position: point,
icon: icon.icon,
shadow: icon.shadow
});
}
});
}
function bindInfoWindow(marker, map, infoWindow) {
google.maps.event.addListener(marker, 'click', function () {
map.setCenter(marker.getPosition());
});
}
function downloadUrl(url, callback) {
var request = window.ActiveXObject ?
new ActiveXObject('Microsoft.XMLHTTP') :
new XMLHttpRequest;
request.onreadystatechange = function () {
if (request.readyState == 4) {
request.önreadystatechange = doNothing;
callback(request, request.status); }
};
request.open('GET', url, true);
request.send(null);
}
function doNothing() { }
</script>
The functions I tried are given below( I called the remove overlays function, remove markers and also the delete markers function before BindMarker() inside the setInterval function so that it would remove the markers before binding new markers
function setMapOnAll(map) {
for (let i = 0; i < markers.length; i++) { markers[i].setMap(map); }
}
function clearMarkers() {setMapOnAll(null); }
function deleteMarkers() {clearMarkers(); markers = [];}
function clearOverlays() {
while(markers.length) { markers.pop().setMap(null); }
markers.length = 0;
}
I believe you've confounded the examples, which your attempted code shows verbatim and what your code is actually doing.
It the sample code you're drawing from, the variable markers is an array of Google Map marker objects. So this code makes sense in that regard:
function deleteMarkers() {clearMarkers(); markers = [];}
But, your code is not creating an array of those objects. Your code does not have a variable named markers that is accessible outside of the downloadUrl() function. The variable markers does show up inside that function, but it contains an HTMLCollection element - not an array of markers.
To fix this you need to create an array named markers right after the variable name marker is created.
Then, within the for loop, just after you create the marker push that marker on to the markers array. You will also have to rename the existing markers variable to something more appropriate like markerElements.
Now, the deleteMarkers() function will work as expected.

not able to get angular 2 scope inside call back functions

I need to update a array object inside a call back function ,i used the following lines but the values are set in the scope of call back loop not as angular variable so my view is not updated.(deviceval) value is changed if i print it inside the callback but outside the value is still the old one.
export class DashboardComponent implements OnInit {
hideTable: boolean = true;
public deviceVal:any;
constructor(private ref: ChangeDetectorRef) {}
ngOnInit() {
this.deviceVal = deviceData;
console.log(this.deviceVal);
var container = $('.map-canvas');
var options = {
center: new google.maps.LatLng(41.676258, -99.683199),
zoom: 4,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
gmap = new google.maps.Map(container[0], options);
this.drawChart(deviceData);
this.plotMarkers();
}
plotMarkers(){
$.each(deviceData, function(key, val) {
var controller=this;
var marker = new google.maps.Marker({
position: new google.maps.LatLng(parseInt(val.lat), parseInt(val.lon)),
map: gmap,
});
google.maps.event.addListener(marker, 'click', function() {
this.deviceVal = val;
});
markerCache.push(marker);
})
}
}
The problem is here:
$.each(deviceData, function(key, val) {
var controller=this;
var marker = new google.maps.Marker({
position: new google.maps.LatLng(parseInt(val.lat), parseInt(val.lon)),
map: gmap,
});
google.maps.event.addListener(marker, 'click', function() {
this.deviceVal = val;
});
markerCache.push(marker);
})
when you use function() as a callback function, the 'this' value is changed. You better read here about this.
You can fix this using arrow functions:
plotMarkers(){
$.each(deviceData, (key, val) => {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(parseInt(val.lat), parseInt(val.lon)),
map: gmap,
});
google.maps.event.addListener(marker, 'click', () => {
this.deviceVal = val;
});
})
}
But you have a lot of other problems, like: you don't need to use jQuery (to be honest, you should avoid jQuery in an ng2 app), the 'gmap' variable is not defined (you can set it as an property of the class, as you have done with 'deviceVal' for example), 'markerCache' was not defined too, there is no drawChart method, 'deviceData' is not defined inside plotMarkers().
I solved it by declaring a global variable before export component like
var controller;
and initialized it in ngoninit(),
controller = this;
and passed the controller to addlistener,
google.maps.event.addListener(marker, 'click', () => {
controller.deviceVal=[];
controller.deviceVal.push(val);
//console.log(controller.deviceVal+"end....................................")
});

accessing marker from listener-Google maps javascript API3.0

I have several markers (in an array) on my map, each with a custom ID tag i've given them.
What I want:
When I click on a marker, i wish to add it's ID to another array.
The problem:
The mouse event from Google does not have a target attribute, only the position, so I can't seem to access the ID directly.
I don't really want to have to resort to using the position to find the closest marker to it and returning it's ID this way, it's rather convoluted.
All help is appreciated
This is really easy, thanks to a feature in JavaScript and many other languages called a closure.
Simply put the code that creates the marker and sets up its event listener(s) insidea function, and call that function for each marker with the data needed for that specific marker. For example:
var places = [
{
id: 'one', lat: 1, lng: -1, name: 'First'
},
{
id: 'two', lat: 2, lng: -2, name: 'Second'
}
];
for( var i = 0; i < places.length; i++ ) {
addPlace( places[i] );
}
function addPlace( place ) {
var marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng( place.lat, place.lng ),
title: place.name
});
google.maps.event.addListener( 'click', function() {
alert( 'Clicked ' + place.id + ': ' + place.name );
});
}
I didn't test this Maps API code, but the specifics of the code are not important. What is important to understand is that place variable you see used in the code. This is the key part: that variable is accessible inside the event listener, simply because the event listener is nested inside the addPlace() function which has place as a parameter.
Note the difference between that code and code like this, which will not work:
for( var i = 0; i < places.length; i++ ) {
var place = places[i];
var marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng( place.lat, place.lng ),
title: place.name
});
google.maps.event.addListener( 'click', function() {
alert( 'Clicked ' + place.id + ': ' + place.name );
});
}
The only difference between the two is that the working version puts the loop body in a separate function which is called from the loop, instead of having all that code directly inside the loop. Having that code in a function that you call each time is what creates the closure, and that's what lets the inner event listener function "see" the variables in the outer function.
The great thing about closures is that you can use them in any similar situation. It isn't specific to the Maps API or the objects that the API uses. You may have even used them already and not realized it, for example in a setTimeout() call like this:
// Display an alert 'time' milliseconds after this function is called
function slowAlert( message, time ) {
setTimeout( function() {
alert( message );
}, time );
}
slowAlert( 'Howdy!', 1000 ); // Wait a second and then say Howdy!
Where the alert() call is made inside the setTimeout() callback function is made, it's using the closure on the slowAlert() function to pick up the value of the message variable that was passed into that function.
This should help. I added a customId property to the marker object and then in the marker click event I assign the id property to the new array.
function initialize() {
var map;
var centerPosition = new google.maps.LatLng(38.713107, -90.42984);
var options = {
zoom: 6,
center: centerPosition,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var bounds = new google.maps.LatLngBounds();
map = new google.maps.Map($('#map')[0], options);
var infoWindow = new google.maps.InfoWindow();
//marker array
var markers = [];
//sencondary array to store markers that were clicked on.
var markerIdArray = [];
for (i = 0; i < 6; i++) {
var lat = 38.713107 + Math.random();
var lng = -90.42984 + Math.random();
var marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(lat, lng),
customId: i //add a custom id to the marker
});
bounds.extend(marker.position);
google.maps.event.addListener(marker, 'click', function () {
//add the id to the other array.
markerIdArray.push(this.customId);
//log the content of the array to the console.
console.log(markerIdArray);
});
markers.push(marker);
}
map.fitBounds(bounds);
}
Here is an example of this in action.

javascript variables and asynchronous calls

I have the following code, and having read this, i understand it wont work because the getJSON call is asynchronous. How do i need to change this so that the MarkerClusterer function gets triggered with a full set of markers? I've tried putting the MarkerClusterer function inside the getJSON call but with no luck...
var mcOptions = {gridSize: 50, maxZoom: 9};
var markers = [];
function parse_json(json) {
if (json.length > 0) {
for (i=0; i<json.length; i++) {
var report = json[i];
var latLng = new google.maps.LatLng(report.latitude, report.longitude);
markers[i] = new google.maps.Marker({
position: latLng,
title: report.name + ' ' + report.surf_size_ft_round,
url: "/place/"+report.slug
});
google.maps.event.addListener(markers[i], 'click', function() {
window.location.href = markers[i].url;
});
markers.push(markers[i]);
}
}
};
$.getJSON('<%= request.fullpath + ".json" %>', function(stream) {
if (stream.length > 0) {
parse_json(stream);
alert(markers[1].title); //sanity check - gives result
}
});
alert(markers[5].title); // sanity check - empty
var mc = new MarkerClusterer(map, markers, mcOptions);
Why not put this code snippet:
mc = new MarkerClusterer(map, markers, mcOptions);
inside the anonymous callback function in your $.getJSON? Just declare var mc; somewhere outside the $.getJSON scope to be able to have access to it elsewhere.
Alternatively, you can fire an event at the end of your parse_json function, listen to that event and then fire up another function that creates your MarkerClusterer object when the event has fired. Check this out: How to trigger event in JavaScript?
EDIT:
Upon inspecting your code a bit more, I can see that you set markers[i] to a new Marker instance and then push onto the markers array that same instance. You probably want to either set markers[i] to a new Marker instance or you want to create a var marker, setting it to a new Marker instance and then pushing on the markers array.
Maybe you need to put it inside the success function you give as an input to $.getJSON?
$.getJSON('<%= request.fullpath + ".json" %>', function(stream) {
if (stream.length > 0) {
parse_json(stream);
alert(markers[1].title); //sanity check - gives result
mc = new MarkerClusterer(map, markers, mcOptions);
}
});
alert(markers[5].title); // sanity check - empty

javascript google maps api remove markers

I have functions that when a li element is clicked markers are added to the map. If another li is clicked then the original markers are removed and the new one appearr.
The issue I am having is that the markers are placed on the map when a li is clicked for the first time. When a second li is clicked the markers are removed but the new ones are not added. I get no error in firebug. I cant see what I am missing.
$(document).ready(function() {
$(".markerSelection").click(function() {
var selectionId = $(this).attr("id");
drop(selectionId);
});
});
var markers = {
shopping : [
new google.maps.LatLng(52.26183, -7.11339),
new google.maps.LatLng(52.26134, -7.11226),
new google.maps.LatLng(52.26067, -7.11181),
new google.maps.LatLng(52.26003, -7.11033)],
cars : [
new google.maps.LatLng(52.26183, -7.11339),
new google.maps.LatLng(52.26134, -7.11226),
new google.maps.LatLng(52.26067, -7.11181),
new google.maps.LatLng(52.26003, -7.11033)]
};
var iterator = 0;
function drop(selectionId) {
clearOverlays();
for (var i = 0; i < markers[selectionId].length; i++) {
setTimeout(function() {
addMarker(selectionId);
}, i * 200);
}
}
function addMarker(selectionId) {
marker = new google.maps.Marker({
position: markers[selectionId][iterator],
map: map,
draggable: false,
animation: google.maps.Animation.DROP
});
iterator++;
markersArray.push(marker);
}
// Removes the overlays from the map, but keeps them in the array
function clearOverlays() {
if (markersArray) {
for (i in markersArray) {
markersArray[i].setMap(null);
}
}
}
I reviewed your code one more time, and I think the problem is with iterator, which is initialized to 0 in global scope. That is why the first time it works okay, but after that, indices exceed. It seems you should set it to zero at the beginning of the drop() function.
However it makes more sense if you pass index as a second parameter of addMarker() instead of an outer variable which is handled in drop() and complicate you code.
You have defined markers as a Json variable, but I don't know what you mean by markers[selectionId]! Markers is not defined as an array and it seems it's not correct to refer to it by index!

Categories