I'm trying to add an event listener to each icon on the map when it's pressed. I'm storing the information in the database and the value that I'm wanting to retrive is "i" however when I output "i", I get it's last value which is 5 (there are 6 objects being drawn onto the map)
Below is the code, what would be the best way to get the value of i, and not the object itself.
var drawLotLoc = function(id) {
var lotLoc = new GIcon(G_DEFAULT_ICON); // create icon object
lotLoc.image = url+"images/markers/lotLocation.gif"; // set the icon image
lotLoc.shadow = ""; // no shadow
lotLoc.iconSize = new GSize(24, 24); // set the size
var markerOptions = { icon: lotLoc };
$.post(opts.postScript, {action: 'drawlotLoc', id: id}, function(data) {
var markers = new Array();
// lotLoc[x].description
// lotLoc[x].lat
// lotLoc[x].lng
// lotLoc[x].nighbourhood
// lotLoc[x].lot
var lotLoc = $.evalJSON(data);
for(var i=0; i<lotLoc.length; i++) {
var spLat = parseFloat(lotLoc[i].lat);
var spLng = parseFloat(lotLoc[i].lng);
var latlng = new GLatLng(spLat, spLng)
markers[i] = new GMarker(latlng, markerOptions);
myMap.addOverlay(markers[i]);
GEvent.addListener(markers[i], "click", function() {
console.log(i); // returning 5 in all cases.
// I _need_ this to be unique to the object being clicked.
console.log(this);
});
}
});
You have an issue with closures. Your functions see i's last valuse. Simply add another closure to fix your error:
for(var i=0; i<lotLoc.length; i++) {
(function(i){
// ...
})(i); //run the function with i as argument
}//for
Related
I'm having a bit of trouble trying to parse the results of an array and print to the console. It's a two part problem actually. When I build the array it's adding "undefined" to the results. When I try to loop through the individual strings in the array it isn't parsing, just returning the full array object.
What I'm trying to do is collect all the field values selected from a list view and write them to another child list as separate items. When displaying results in a console it shows as an object array. When I run the typeof method against it I believe it shows as a string.
To reiterate, why am I getting undefined and why is my array not printing to console correctly. Below is an example of what is being returned thus far (when two records are selected) and my code.
Results:
undefinedDaffy DuckBugs Bunny
undefined
Code:
// Grabs selected items from getSelected function and passes parameters to writeSelected function
function callAccepted() {
getSelected().done(function(varObjects) {
for (var k in varObjects) {
console.log(varObjects[k]);
}
}); // End getSelected
} // End callAccepted
// Grabs selected items, accepts input from callAccepted or callRejected functions
function getSelected() {
var dfd = $.Deferred(function(){
var ctx = SP.ClientContext.get_current();
var clientContext = new SP.ClientContext();
var targetList = clientContext.get_web().get_lists().getByTitle(ListName);
var SelectedItems = SP.ListOperation.Selection.getSelectedItems(ctx);
var items = [];
var arrItems = [];
for (var i in SelectedItems) {
var id = SelectedItems[i].id;
var item = targetList.getItemById(id);
clientContext.load(item, "Title");
items.push(item);
} // End for
clientContext.executeQueryAsync(
function(){ // Return to button click function
var itemLength = 0;
var itemObjects = [];
for (var j = 0; j < items.length; j++) {
itemObjects = items[j].get_item("Title");
itemLength += itemObjects;
arrItems.push(itemObjects);
}
dfd.resolve(arrItems, itemLength);
},
function(){ // Return to button click function
dfd.reject(args.get_message());
}
); // End ClientContext
}); // End dfd
return dfd.promise();
} // End getSelected
Why are you writing "var itemObjects;" in 1 line and add one string "itemObjects += items[j].get_item("Title");" in another? There'll be only 1 string anyway, so when you change those 2 lines into one, "undefined" should disappear:
function callAccepted() {
getSelected().done(function(varObjects, iLength) {
// Stuff
for (var k = 0; k < iLength; k++) {
console.log(varObjects[k]);
}
}); // End getSelected
} // End callAccepted
// Get user information function
function getSelected() {
var dfd = $.Deferred(function(){
var ctx = SP.ClientContext.get_current();
var clientContext = new SP.ClientContext();
var targetList = clientContext.get_web().get_lists().getByTitle(ListName);
var SelectedItems = SP.ListOperation.Selection.getSelectedItems(ctx);
var items = [];
var arrItems = [];
for (var i in SelectedItems) {
var id = SelectedItems[i].id;
var item = targetList.getItemById(id);
clientContext.load(item, "Title");
items.push(item);
} // End for
clientContext.executeQueryAsync(
function(){ // Return to button click function
for (var j = 0; j < items.length; j++) {
var itemObjects = items[j].get_item("Title");
var itemLength = items.length;
arrItems.push(itemObjects);
}
dfd.resolve(arrItems, itemLength);
},
function(){ // Return to button click function
dfd.reject(args.get_message());
}
); // End ClientContext
}); // End dfd
return dfd.promise();
} // End getSelected
The reason for this is that after creating the variable without any value, it's undefined, so += 'Unicorn' will give us ugly 'UndefinedUnicorn'. If you wish to make variable for this purpose, write it "var x = ''".
And if - for example - you want to sum length of all "items", then this one function should look like:
function(){ // Return to button click function
var itemLength = 0;
for (var j = 0; j < items.length; j++) {
var itemObjects = items[j].get_item("Title");
itemLength += itemObjects;
arrItems.push(itemObjects);
}
dfd.resolve(arrItems, itemLength);
}
But I'm not exactly sure what are you trying to get here.
This question already has answers here:
Google Maps JS API v3 - Simple Multiple Marker Example
(15 answers)
Closed 8 years ago.
I am trying to add 3 markers to a map and when click on the markers, an info window will be shown. But every array element inside google.maps.event.addListener becomes undefined.
What's the problem?
<div id="map-canvas2" style="width:100%;height:500px"></div>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp"></script>
<script>
var num;
var marker;
var infoWindow;
var infoText;
var lat;
var lng;
var map;
function initialize() {
num = 3;
marker = [];
infoWindow = [];
infoText = [];
lat = [];
lng = [];
infoText[0] = "test1";
lat[0] = 22.420845;
lng[0] = 114.208705;
infoText[1] = "test2";
lat[1] = 22.416026;
lng[1] = 114.209321;
infoText[2] = "test3";
lat[2] = 22.420841;
lng[2] = 114.205188;
for (var i = 0; i < num; i++) {
marker[i]=new google.maps.Marker({
position:new google.maps.LatLng(lat[i], lng[i]),
});
infoWindow[i] = new google.maps.InfoWindow({
content:"<div>"+infoText[i]+"</div>"
});
}
var mapOptions = {
zoom: 17,
center: new google.maps.LatLng(22.420458,114.207482)
};
map = new google.maps.Map(document.getElementById('map-canvas2'), mapOptions);
for (var i = 0; i < num; i++) {
marker[i].setMap(map);
google.maps.event.addListener(marker[i], 'click', function() {
new google.maps.InfoWindow({
content:"<div>"+infoText[i]+"</div>"
}).open(map,marker[i]);
});
}
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
The problem:
Each event listener is referencing the same variable i which gets incremented on each pass of the for loop. So after the loop is finished the value of i is 3, but none of your arrays have an index of 3 so you get undefined. Because each event handler is referencing the same i variable they are all referencing the same undefined array values.
The solution: Create a closure so that the event handler for each marker has a it's own variable instead sharing reference to single variable.
for (var i = 0; i < num; i++) {
marker[i].setMap(map);
google.maps.event.addListener(marker[i], 'click', (function(index) {
return function() {
new google.maps.InfoWindow({
content: "<div>"+infoText[index]+"</div>"
}).open(map, marker[index]);
}
})(i));
}
What we're doing is creating a Immediately-Invoked Function Expression "IIFE". The IIFE has a parameter called index which is set to the value of i. Because variables have function scope, index belongs only to this function. Inside the IIFE we return a function that will do the actual work when the event is triggered, but it will reference index not i.
Don't send indexed parameters to an anonymous function:
for (var i = 0; i < num; i++) {
var mrk = marker[i];
var iwContent = infoText[i];
mrk.setMap(map);
google.maps.event.addListener(mrk, 'click', function() {
new google.maps.InfoWindow({
content:"<div>"+iwContent+"</div>"
}).open(map,mrk);
});
}
http://rca2.com/mapping/thispageblinks.htm
http://rca2.com/mapping/doesnotremove.htm
The second example really doesn't do anything without continuously updated xml data.
I'm converting (finally!) my map applications from Google v2 to v3. In v2, the application read in xml data every 5 seconds, cleared markers, then new markers were created and placed on the map. The ability to clear the map overlay using map.clearOverlays() no longer exists in v3. The suggested solution is to keep track of the old markers, then remove them. Clearing the markers in a loop prior to creating new markers is easy to do, and works. Except for the fact that the markers blink when replaced more often than not. This is very distracting, and highly undesirable since this did not happen in v2.
I decided that I should compare the new marker data to the old marker data. If the location and icon color stayed the same, both old and new markers are basically ignored. For the sake of clarity, the icon color signifies a status of the vehicle represented by the icon. In this case the application is to track ambulance activity, so green would be available, blue would be en-route, etc.
The code handles the checking of the new and old markers fine, but for some reason, it will never remove the old marker when a marker (unit) moves. I saw suggestions about setMap() being asynchronous. I also saw suggestions about the arrays not being google.maps.Marker objects. I believe that my code handles each of these issues correctly, however the old markers are still never removed.
I've also made sure that my marker arrays are global variables. I am also using the variable side_bar_html to display information about which markers were supposed to be removed, and which markers were supposed to be added. The added markers are being added just fine. I just don't know where to turn next. Any help you could offer would be greatly appreciated.
function getMarkers() {
// create a new connection to get our xml data
var Connect = new XMLHttpRequest();
// send the get request
Connect.open("GET", xml_file, false);
Connect.setRequestHeader("Content-Type", "text/xml");
Connect.send(null);
// Place the response in an XML document.
var xmlDoc = Connect.responseXML;
// obtain the array of markers and loop through it
var marker_data = xmlDoc.documentElement.getElementsByTagName("marker");
// hide the info window, otherwise it still stays open where a potentially removed marker used to be
infowindow.close();
// reset the side_bar and clear the arrays
side_bar_html = "";
markerInfo = [];
newMarkers = [];
remMarkers = [];
addMarkers = [];
// obtain the attributes of each marker
for (var i = 0; i < marker_data.length; i++) {
var latData = marker_data[i].getAttribute("lat");
var lngData = marker_data[i].getAttribute("lng");
var minfo = marker_data[i].getAttribute("html");
var name = marker_data[i].getAttribute("label");
var icontype = marker_data[i].getAttribute("icontype");
var unitNum = marker_data[i].getAttribute("unitNum");
var llIcon = latData + lngData + icontype;
zIndexNum = zIndexNum + 1;
// create the new marker data needed
var myLatLng = new google.maps.LatLng(parseFloat(latData), parseFloat(lngData));
var marker = {
position: myLatLng,
icon: gicons[icontype],
title: "",
unitIcon: unitNum,
unitLLIData: llIcon,
zIndex: zIndexNum
};
// add a line to the side_bar html
// side_bar_html += '<a href="javascript:myclick(' + i + ')">' + name + '<\/a><br />';
// add an event listeners on the marker
addInfoWindow(marker, minfo);
// save the current data for later comparison
markerInfo.push(minfo);
newMarkers.push(marker);
}
// now loop thru the old marker data and compare to the new, to see if we need to remove any old markers
var refreshIt = true;
var removeIt = true;
var currNumber = "";
var currLLIcon = "";
var lastNumber = "";
var lastLLIcon = "";
for (var i = 0; i < newMarkers.length; i++) {
currNumber = newMarkers[i].unitIcon;
currLLIcon = newMarkers[i].unitLLIData;
for (var j = 0; j < oldMarkers.length; j++) {
refreshIt = true;
lastNumber = oldMarkers[j].unitIcon;
lastLLIcon = oldMarkers[j].unitLLIData;
if (lastNumber == currNumber) {
if (currLLIcon == lastLLIcon) {
refreshIt = false;
} else {
refreshIt = true;
remMarkers.push(oldMarkers[j]);
}
break;
}
}
// if we need to refresh a marker, add it to our new array here
if (refreshIt == true) {
addMarkers.push(newMarkers[i]);
}
}
// then loop thru and see if any units are no longer on the map
for (var j = 0; j < oldMarkers.length; j++) {
removeIt = true;
lastNumber = oldMarkers[j].unitIcon;
for (var i = 0; i < newMarkers.length; i++) {
currNumber = newMarkers[i].unitIcon;
if (lastNumber == currNumber) {
removeIt = false;
break;
}
}
// if we need to refresh a marker, add it to our new array here
if (removeIt == true) {
remMarkers.push(oldMarkers[j]);
}
}
// now loop thru the old markers and remove them
for (var i = 0; i < remMarkers.length; i++) {
var marker = new google.maps.Marker(remMarkers[i]);
marker.setMap(null);
side_bar_html += 'removing ' + remMarkers[i].unitIcon + '<br />';
}
// then loop thru the new markers and add them
for (var i = 0; i < addMarkers.length; i++) {
var marker = new google.maps.Marker(addMarkers[i]);
marker.setMap(map);
side_bar_html += 'adding ' + addMarkers[i].unitIcon + '<br />';
}
// and last save the old markers array into oldMarkers
oldMarkers = [];
for (var i = 0; i < newMarkers.length; i++) {
oldMarkers.push(newMarkers[i]);
}
// put the assembled side_bar_html contents into the side_bar div, then sleep
document.getElementById("side_bar").innerHTML = side_bar_html;
setTimeout('getMarkers()', 5000);
}
For context purposes, here is the code that does clear the old markers, but many (not all) or the markers blink when refreshed, even if they don't in fact move loaction.
function getMarkers() {
// create a new connection to get our xml data
var Connect = new XMLHttpRequest();
// send the get request
Connect.open("GET", xml_file, false);
Connect.setRequestHeader("Content-Type", "text/xml");
Connect.send(null);
// Place the response in an XML document.
var xmlDoc = Connect.responseXML;
// obtain the array of markers and loop through it
var marker_data = xmlDoc.documentElement.getElementsByTagName("marker");
// hide the info window, otherwise it still stays open where the removed marker used to be
infowindow.close();
// now remove the old markers
for (var i = 0; i < oldMarkers.length; i++) {
oldMarkers[i].setMap(null);
}
oldMarkers.length = 0;
// reset the side_bar and clear the arrays
side_bar_html = "";
markerInfo = [];
newMarkers = [];
// obtain the attributes of each marker
for (var i = 0; i < marker_data.length; i++) {
var latData = marker_data[i].getAttribute("lat");
var lngData = marker_data[i].getAttribute("lng");
var minfo = marker_data[i].getAttribute("html");
var name = marker_data[i].getAttribute("label");
var icontype = marker_data[i].getAttribute("icontype");
var unitNum = marker_data[i].getAttribute("unitNum");
zIndexNum = zIndexNum + 1;
// create the new marker data needed
var myLatLng = new google.maps.LatLng(parseFloat(latData), parseFloat(lngData));
var marker = new google.maps.Marker({
position: myLatLng,
icon: gicons[icontype],
title: "",
unitIcon: unitNum,
zIndex: zIndexNum
});
// add a line to the side_bar html
side_bar_html += '<a href="javascript:myclick(' + i + ')">' + name + '<\/a><br />';
// add an event listeners on the marker
addInfoWindow(marker, minfo);
// save the current data for later comparison
markerInfo.push(minfo);
newMarkers.push(marker);
oldMarkers.push(marker);
}
// now add the new markers
for (var i = 0; i < newMarkers.length; i++) {
newMarkers[i].setMap(map);
}
// put the assembled side_bar_html contents into the side_bar div, then sleep
document.getElementById("side_bar").innerHTML = side_bar_html;
setTimeout('getMarkers()', 5000);
}
Finally figured out the solution. The process was reading in new xml data which was compared to the saved xml data, to determine if a marker needed to be moved or displayed in a different color on the map.
When I created a new marker object, I did not set the map: property, because I needed to compare the lat/lon/color of the new object to the old before I determined whether a marker needed to be moved. The problem was the map: property not being set. I save the marker data without the map: property set into the new marker array, then copied the new marker array into old marker array to do the next comparison. I should have copied the old marker object into the new marker array! The old marker object HAD the map: property set, and that allowed the Google mapping code to know which marker I wanted to remove.
Sorry for the stupid mistake, but I'm pretty new to Javascript.
Rich
The problem I am having is when im pushing or unshifting items into array they are being automatically grouped, very strange:im guessing my loops are not well put together, more or less im experimenting.any help is appreciated.
var i;
var anObject:/*is created dynamically in a loop for instance I load random images
and push into ' myArray'. My myaArray should
look like this:["image1,image3,image2,image3,image2] but instead
im getting:["image1,image3,image3,image2,image2] there grouping up for some reason,
But i dont want them too*/
var BookH = [];
function create(){
for (var i=0; i<10; i++){
var object;
var tempObject;
var loader = new THREE.OBJMTLLoader();
loader.load( 'mod/'+mysqlVars[i].Format+'.obj','mod/bookH.mtl') ;
loader.addEventListener( 'load', function ( event ) {
object = event.content;
for(k in object.children){
object.children[k].castShadow = true;
object.children[k].receiveShadow = false;
}
tempObject = object.clone();
BookH.push(tempObject);
console.log('mod/'+mysqlVars[9].Format);
if(BookH.length == 10){
populate();
}
});
}
/////////counter loop///
}
I'm trying to loop through 3 images in my array and add it to the stage (using easelJS). I want to position it as well. When I try to access the images in the array i get an error saying that the I can't set x of undefined. Why can't the x variable of the easeljs Bitmap be accessed?
function displayPosters() {
getPosters(); //get all images and assign it to designated array
console.log(frontPosters);
console.log(frontPosters.length);
if(currentCat == Category.HOME) { //if current category is HOME
for(var i = 0; i < frontPosters.length; i++) { //loop through posters
frontPosters[i].x = 40; //set x position for testing, also where error occurs
stage.addChild(frontPosters[i]); //add poster to stage
}
}
}
here is the code for loading and pushing those images into the frontPosters arrray.
var frontPosters = new Array(3);
function getPosters() {
var games = new Image(); //create 3 images
var apps = new Image();
var aboutme = new Image();
games.onload = function() { //add image to frontImages array on load
var gamesPost = new createjs.Bitmap(games);
frontPosters[0] = gamesPost;
};
apps.onload = function() {
var appPost = new createjs.Bitmap(apps);
frontPosters[1] = appPost;
};
aboutme.onload = function() {
var amPost = new createjs.Bitmap(aboutme);
frontPosters[2] = amPost;
};
games.src = "images/assets/posters/games_poster.jpg";
apps.src = "images/assets/posters/apps_poster.jpg";
aboutme.src = "images/assets/posters/aboutme_poster.jpg";
}
Using for(poster in frontPosters) is bad practice, because you're actually iterating over the Array Object and not the values (a.k.a. the Array itself). Use for(var i=0; i<frontPosters.length; i++) instead. It's the easiest solution, IMO.
for(var i = 0; i < frontPosters.length; i++) { //loop through posters
frontPosters[i].x = 40; //set x position for testing, also where error occurs
stage.addChild(frontPosters[i]); //add poster to stage
}
Edit
I think you are dealing with a race-condition. You are going over your array before all images were loaded. By setting var frontPosters = new Array(3); you automatically set three values to undefined which are pushed into the new array.
You should check that all images were loaded before proceeding with the script.
Here's an idea for you. Set a callback that will run only after the third image was loaded.
var frontPosters = new Array(3);
function getPosters(callback) {
var games = new Image(),
apps = new Image(),
aboutme = new Image(),
loaded = 0;
games.onload = function() {
frontPosters[0] = new createjs.Bitmap(this);
if (++loaded === 3) callback();
};
apps.onload = function() {
frontPosters[1] = new createjs.Bitmap(this);
if (++loaded === 3) callback();
};
aboutme.onload = function() {
frontPosters[2] = new createjs.Bitmap(this);
if (++loaded === 3) callback();
};
games.src = "images/assets/posters/games_poster.jpg";
apps.src = "images/assets/posters/apps_poster.jpg";
aboutme.src = "images/assets/posters/aboutme_poster.jpg";
}
function displayPosters() {
getPosters(function() {
if(currentCat == Category.HOME) { //if current category is HOME
for(var i = 0; i < frontPosters.length; i++) { //loop through posters
frontPosters[i].x = 40; //set x position for testing, also where error occurs
stage.addChild(frontPosters[i]); //add poster to stage
}
}
}); //get all images and assign it to designated array, only after all images were loaded
}