0

I think I have an order of events issue that I am having trouble tracking down. For background, you can check out this stack question here. HE was a hero and pointed out an extra ' in my json fro my DB and I was able to fix that, and a silly re-naming of a couple of variables.

His JSFiddle works great but the only difference is I have an AJAX call instead of hard coding the JSON in there. When I run the code with JSON I do not get anything populating on my map, so I put a bunch of consoloe.log() statements in there to see what was going on. I suspect the function to load markers is running before the ajax call.

var gmarkers1 = [];
var markers1 = [];
var markerCluster;
var infowindow;
var lastmarker = null;
var xmlhttp = new XMLHttpRequest();
var url = "myJSONCode.php";


var SawtoothPassTrailhead = {
  name: "Sawtooth Pass Trailhead",
  lat:  36.453165,
  long:  -118.596751,
  type: "backpacking", 

  //Title then link
  seekAdventure: [],
  blogs: ['Mineral King Loop – Sequoia National Park (45 Mile Loop) - Backpackers Review' , 'https://backpackers-review.com/trip-reports/sequoia-mineral-king/'],
  youtTube: []
};

//Call PHP file and get JSON
xmlhttp.onreadystatechange = function() {
    console.log("order 1");
    if (this.readyState == 4 && this.status == 200) {
        myFunction(this.responseText);
        console.log("order 2");
    }
}
xmlhttp.open("GET", url, true);
xmlhttp.send();

var markers2 = new Array();


function myFunction(response) {
  var arr = JSON.parse(response);
  var i;
  var localTrailHeadID;
  var trailHeadCounter = 0;
  var TrailHeadObject;
  var lastTrailHeadID = 0;

  //set array conts all to zero
  var seekAdventureCount;
  var blogsCount;
  var youtubeCount;

  var j = 0;


  //for each row returned by mySQL
  for (i = 0; i < arr.length; i++) {
    localTrailHeadID = arr[i].TrailHeadID;

    //if previuse trailhead is the same as the current trail head get info and add to correct array
    if (localTrailHeadID == lastTrailHeadID) {
      if (arr[i].GuideMediaType == "SeekAdventure") {
        TrailHeadObject.seekAdventureGuideList[seekAdventureCount] = arr[i].GuideTitle;
        seekAdventureCount = seekAdventureCount + 1;
        TrailHeadObject.seekAdventureGuideList[seekAdventureCount] = arr[i].GuideLink;
        seekAdventureCount = seekAdventureCount + 1;
      }
      if (arr[i].GuideMediaType == "blog") {
        TrailHeadObject.blogGuideList[blogsCount] = arr[i].GuideTitle;
        blogsCount = blogsCount + 1;
        TrailHeadObject.blogGuideList[blogsCount] = arr[i].GuideLink;
        blogsCount = blogsCount + 1;
      }
      if (arr[i].GuideMediaType == "YouTube") {
        TrailHeadObject.youTubegGuideList[youtubeCount] = arr[i].GuideTitle;
        youtubeCount = youtubeCount + 1;
        TrailHeadObject.youTubegGuideList[youtubeCount] = arr[i].GuideLink;
        youtubeCount = youtubeCount + 1;
      }

    }

    //create new object and then add guide to correct array
    else {

      //add object to array of markers except on first round
      if (j == 0) {
        j = j + 1;
      } else {
        markers1[trailHeadCounter] = TrailHeadObject;
        console.log(trailHeadCounter);

        trailHeadCounter = trailHeadCounter + 1;
      }

      //create new trailhead object
      TrailHeadObject = new Object();

      //set array counters to zero
      var seekAdventureCount = 0;
      var blogsCount = 0;
      var youtubeCount = 0;

      //set name lat and long
      TrailHeadObject.name = arr[i].TrailHeadName;
      TrailHeadObject.lat = arr[i].TrailHeadLat;
      TrailHeadObject.long = arr[i].TrailHeadLong;

      //set TrailHeadObject Guide arrays to empty
      TrailHeadObject.seekAdventureGuideList = [];
      TrailHeadObject.blogGuideList = [];
      TrailHeadObject.youTubegGuideList = [];

      //Add trail Guide
      //check first guide media type and add to correct Array
      if (arr[i].GuideMediaType == "SeekAdventure") {
        TrailHeadObject.seekAdventureGuideList[seekAdventureCount] = arr[i].GuideTitle;
        seekAdventureCount = seekAdventureCount + 1;
        TrailHeadObject.seekAdventureGuideList[seekAdventureCount] = arr[i].GuideLink;
        seekAdventureCount = seekAdventureCount + 1;
      }
      if (arr[i].GuideMediaType == "blog") {
        TrailHeadObject.blogGuideList[blogsCount] = arr[i].GuideTitle;
        blogsCount = blogsCount + 1;
        TrailHeadObject.blogGuideList[blogsCount] = arr[i].GuideLink;
        blogsCount = blogsCount + 1;
      }
      if (arr[i].GuideMediaType == "YouTube") {
        TrailHeadObject.youTubegGuideList[youtubeCount] = arr[i].GuideTitle;
        youtubeCount = youtubeCount + 1;
        TrailHeadObject.youTubegGuideList[youtubeCount] = arr[i].GuideLink;
        youtubeCount = youtubeCount + 1;
      }

    } // end else statement

    //set last trailhead ID
    lastTrailHeadID = localTrailHeadID;
  } //end for Loop
} //end my function

//Proceses JSON Info and build Objects and place into markers1 arrray

///////////////////////////////
//add Hike Objects to Array////
///////////////////////////////
/**
 * Function to init map
 */

// Before we go looking for the passed parameters, set some defaults
// in case there are no parameters
var id;
var index = -1;

//set initial map values
var lat = 40.534900;
var lng = -101.343789;
var zoom = 4;

// If there are any parameters at eh end of the URL, they will be in  location.search
// looking something like  "?marker=3"

// skip the first character, we are not interested in the "?"
var query = location.search.substring(1);

// split the rest at each "&" character to give a list of  "argname=value"  pairs
var pairs = query.split("&");
for (var i = 0; i < pairs.length; i++) {
  // break each pair at the first "=" to obtain the argname and value
  var pos = pairs[i].indexOf("=");
  var argname = pairs[i].substring(0, pos).toLowerCase();
  var value = pairs[i].substring(pos + 1).toLowerCase();

  // process each possible argname  -  use unescape() if theres any chance of spaces
  if (argname == "id") {
    id = unescape(value);
  }
  if (argname == "marker") {
    index = parseFloat(value);
  }
  if (argname == "lat") {
    lat = parseFloat(value);
  }
  if (argname == "lng") {
    lng = parseFloat(value);
  }
  if (argname == "zoom") {
    zoom = parseInt(value);
  }
  if (argname == "type") {
    // from the v3 documentation 8/24/2010
    // HYBRID This map type displays a transparent layer of major streets on satellite images. 
    // ROADMAP This map type displays a normal street map. 
    // SATELLITE This map type displays satellite images. 
    // TERRAIN This map type displays maps with physical features such as terrain and vegetation. 
    if (value == "m") {
      maptype = google.maps.MapTypeId.ROADMAP;
    }
    if (value == "k") {
      maptype = google.maps.MapTypeId.SATELLITE;
    }
    if (value == "h") {
      maptype = google.maps.MapTypeId.HYBRID;
    }
    if (value == "t") {
      maptype = google.maps.MapTypeId.TERRAIN;
    }
  }
}

function makeLink() {
  var mapinfo = "lat=" + map.getCenter().lat().toFixed(6) +
    "&lng=" + map.getCenter().lng().toFixed(6) +
    "&zoom=" + map.getZoom() +
    "&type=" + MapTypeId2UrlValue(map.getMapTypeId());
  if (lastmarker) {
    var a = "https://www.seekadventure.net/adventureMap.html?id=" + lastmarker.id + "&" + mapinfo;
    var b = "https://www.seekadventure.net/adventureMap.html?marker=" + lastmarker.index + "&" + mapinfo;
  } else {
    var a = "https://www.seekadventure.net/adventureMap.html?" + mapinfo;
    var b = a;
  }

  document.getElementById("idlink").innerHTML = '<a href="' + a + '" id=url target=_new>Share Current Map View</a>';
}

function MapTypeId2UrlValue(maptype) {
  var urlValue = 'm';
  switch (maptype) {
    case google.maps.MapTypeId.HYBRID:
      urlValue = 'h';
      break;
    case google.maps.MapTypeId.SATELLITE:
      urlValue = 'k';
      break;
    case google.maps.MapTypeId.TERRAIN:
      urlValue = 't';
      break;
    default:
    case google.maps.MapTypeId.ROADMAP:
      urlValue = 'm';
      break;
  }
  return urlValue;
}
//----------------------------------------------------------
//initialize map 
function initialize() {
  console.log("initialize map");
  var center = new google.maps.LatLng(lat, lng);
  var mapOptions = {
    zoom: zoom,
    center: center,
    mapTypeId: google.maps.MapTypeId.TERRAIN
  };

  map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
  for (i = 0; i < markers1.length; i++) {
    console.log("Adding Markers to map");
    addMarker(markers1[i]);
  }

  infowindow = new google.maps.InfoWindow({
    content: ''
  });

  // Make the link the first time when the page opens
  lastmarker = null;
  makeLink();

  // Make the link again whenever the map changes
  google.maps.event.addListener(map, 'maptypeid_changed', makeLink);
  google.maps.event.addListener(map, 'center_changed', makeLink);
  google.maps.event.addListener(map, 'bounds_changed', makeLink);
  google.maps.event.addListener(map, 'zoom_changed', makeLink);

  google.maps.event.addListener(map, 'click', function() {
    lastmarker = null;
    makeLink();
    infowindow.close();
  });
}

/**
 * Function to add marker to map
 */

function addMarker(marker) {
  var category = marker.type;
  var title = marker.name;
  var pos = new google.maps.LatLng(marker.lat, marker.long);
  var content = BuildBubbleHTML(marker);

  marker1 = new google.maps.Marker({
    title: title,
    position: pos,
    category: category,
    map: map
  });

  gmarkers1.push(marker1);

  // Marker click listener
  google.maps.event.addListener(marker1, 'click', (function(marker1, content) {
    return function() {
      infowindow.setContent(content);
      infowindow.open(map, marker1);
      map.panTo(this.getPosition());
      //map.setZoom(15);
    }
  })(marker1, content));
}

/////////////////////////
///Functions For Links///
/////////////////////////

//put pop up bubble html together 
function BuildBubbleHTML(hike) {
  html = "";
  html = html + '<h6>' + hike.name + '</h6>';
  //If Seek Adventure Links Exist
  if (hike.seekAdventureGuideList.length > 0) {
    seekAdventureHTML = '<p>Seek Adventure Links</p>';
    seekAdventureHTML = seekAdventureHTML + '<ul>'
    var i;
    for (i = 0; i < hike.seekAdventureGuideList.length; i += 2) {
      seekAdventureHTML = seekAdventureHTML + '<li>';
      seekAdventureHTML = seekAdventureHTML + '<a href="' + hike.seekAdventureGuideList[i + 1] + '"target="_blank">';
      seekAdventureHTML = seekAdventureHTML + hike.seekAdventureGuideList[i] + '</a></li>';
    }
    seekAdventureHTML = seekAdventureHTML + '</ul>';
    html = html + seekAdventureHTML;
  }

  //If Blog Links Exist
  if (hike.blogGuideList.length > 0) {
    blogHTML = '<p>Blog Links</p>';
    blogHTML = blogHTML + '<ul>'
    var i;
    for (i = 0; i < hike.blogGuideList.length; i += 2) {
      blogHTML = blogHTML + '<li>';
      blogHTML = blogHTML + '<a href="' + hike.blogGuideList[i + 1] + '""target="_blank">';
      blogHTML = blogHTML + hike.blogGuideList[i] + '</a></li>';
    }
    blogHTML = blogHTML + '</ul>';
    html = html + blogHTML;
  }
  return html;
};

When I execute this code, the output I get in my console looks like this:

Order 1
initialize map
order 1
order 1
order 1
0
1
2
3
4
5
6
7
order 2

I am not sure why Order 1 is run multiple times. Order 1 is in my xmlhttp.onreadystatechange = function()

I would expect the output of my console log to be more like:

Order 1
0
1
2
3
4
5
6
7
Order 2
Initialize map

Because I need to get all the dat from my DB, parse the JSON, build the new markers array and then add it to the map.

Ten Digit Grid
  • 1,315
  • 4
  • 22
  • 43
  • 1
    AJAX is asynchronous. Initialize the map in the AJAX callback function. – geocodezip Sep 21 '19 at 18:22
  • Make sense.... I am assuming my map is initialized from my html page with this line: can I just put the initialize function in my ajax function? – Ten Digit Grid Sep 21 '19 at 18:24
  • @geocodezip I think that fixed it, not sure if I fixed it in the cleanest way but its seems to work. I just called the initialize function again at the end of my AJAX callback function. – Ten Digit Grid Sep 21 '19 at 18:33
  • You now have a race condition, calling `initialize` twice addresses that. You want to init the map when both the AJAX data has arrived (when the callback function runs) and the Google Maps Javascript API v3 has loaded (the `&callback=initialize` runs. Firing them both off in parallel, the last one that runs will work. You could make the operations serial: call a loader function to load the API in the AJAX callback that then calls `initialize` or make the AJAX request in the `initialize` function that just updates the map with the markers once the data has loaded. Both options take longer. – geocodezip Sep 21 '19 at 20:18

1 Answers1

1

That is because XMLHttpRequest.onreadystatechange is called multiple times, it is called every time readyState changes. It can be called when the connection is opened, when you receive the response headers, when the response body starts to be sent and when the response body has beed received. That is why there is a check in there for readyState == 4. 4 is XMLHttpRequest.DONE.

To make sure initialize is called before the AJAX response comes in, you can place the xmlhttp.send() call at the end of that function.

Titus
  • 22,031
  • 1
  • 23
  • 33