I've been trying to display Bing Map whenever a user clicks on a certain HTML element, but Microsoft.Maps.Map always returns undefined even after setting a timeInterval, I have also tried inserting a new scripts with onscriptload onclick event.
Below are my sample code.
$(document).ready(function(){
var script = document.createElement("script");
script.type = 'text/javascript';
script.src = 'https://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&onscriptload=getMap';
document.head.appendChild(script);
})
$("#showMap").click(function(){
getMap();
})
function getMap(){
console.info(Microsoft.Maps.Map);
var time = "";
var map
var mapControl = Microsoft.Maps.Map;
if(mapControl == undefined){
time = setInterval(function(){ getMap() }, 8000);
}
else{
map = new Microsoft.Maps.Map(document.getElementById('address1_composite'), {credentials: 'Cred'});
clearInterval(time);
}
}
Sorry I know this question is already asked here in this link, Bing map is undefined after loading Javascript file
We have the same scenario but mine is not working as expected though i have tried the suggested answer.
What you should do instead of creating the map when an element is clicked is load the map once and then hide/show it when the button is clicked. This will significantly reduce the number of map loads which means less transactions are generated and thus lower costs.
Also, instead of loading the map script on demand like you are, use the Bing Maps V8 control and load it asynchronously: https://msdn.microsoft.com/en-us/library/mt712557.aspx
Related
Problem: It seems like when loading the Google Maps script, the callback is not performed. It might be related to how to load the Google Maps script.
Context: My website displays a Map in the frontpage when certain minimum viewport conditions are met. Specifically, I avoid loading it when I'm on mobile. I do not want to use jQuery :)
Implementation: The way I'm doing it is by first loading a small handcrafted script (google-map.js), which does a bunch of things and ends with:
function dynamicallyLoadScript(url) {
var script = document.createElement("script");
script.src = url;
script.type = "text/javascript";
script.defer = true;
document.head.appendChild(script);
};
// Load Google Maps when NOT on mobile
if ( ! mobile ) {
dynamicallyLoadScript('https://maps.googleapis.com/maps/api/js?key=wIzaScChUs5NxF3Z5LZoxkAS4wca9A7Pk53I024&callback=initMap');
}
(The JS key is fake).
Outcome: This actually works when I load the site for the first time. I see that google-map.js is loaded, the Google Maps script tag is added to the head, and both resources are loaded and working well. But sometimes (!) when I refresh, the map stops loading and upon inspection I see that the script tag was added successfully, and the resource is loaded, but the initMap() callback function hasn't triggered. If I go to the console, it does fire.
Since this happens only sometimes (and never upon a fresh load without cache), I think it might have something to do with a race condition or just with how cache works. But I'm not sure how to address it. Thoughts?
Solution in a nutshell: This is not a very elegant solution imho, but I figured that what I needed to create is some retry logic for loading initMap() when it runs before it should. In other words, I'm diagnosing this as a race condition present when cache is available.
Implementation: Added a small function to check every 0.1 seconds if the DOM has an element that only exists when the map is fully loaded. When so, the timer stops, but otherwise, it runs the initMap() function.
function checkMaps() {
if (document.getElementsByClassName("gm-style").length == 0) {
initMap();
} else {
clearInterval(checker);
}
}
checker = setInterval(checkMaps, 100);
I've been getting the warning message
You have included the Google Maps API multiple times on this page.
This may cause unexpected errors.
Each time I open a different tab of my website, as each tab has its own map to show to the users.
The way I've made the code to call the google API was this:
function loadMapScript() {
var script = document.createElement("script");
script.src = "https://maps.google.com/maps/api/js";
script.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(script);
}
And I call it here:
$(document).ready(function () {
loadMapScript();
... ... ...
The website has various tabs and when each one of them opens, the script is called, hence why it's there multiple times, I got that far.
What I didn't get is how I stop this from happening, I've tried to perform a few verification's inside the loadMapScript function but they did not work at all. I'd like to know if someone knows a way to make this verification inside the loadMapScript function, to prevent it from adding the google API script more than once.
I faced the same type of problem. This occurs if your web page including maps api more than one time.
I have checked in my case there was a .js file that was also calling maps api, so please first check if you are including maps api more than one time, if you are, remove the one.
I figured it out, it required me to do the following verification, it was simpler than I thought:
var children = document.getElementsByTagName("head")[0].childNodes;
for (i = 0; i < children.length; i++) {
if (children[i].src == script.src) {
found = true;
break;
}
}
if (found == false) { document.getElementsByTagName("head")[0].appendChild(script); }
Thanks for the attempt to help at least
I'm trying to dynamically generate a Google map when the user submits a form, but the map does not appear in its div when the form is submitted.
I tested to see if the map populates on merely on pageload and it does, but when trying to use a div that displays onclick the map does not show.
<form onsubmit="return false" action="">
<input type="text" id="addLocation"/>
<button onclick="findLocation()" id="btnLocation">Find Location</button>
</form>
<div id="mapContainer"></div>
This is the JavaScript:
function findLocation(){
var inputString = $('#addLocation').val();
$('#mapContainer').html('<div id="mapCanvas"></div>');
var apiRequest = $.ajax({
url: 'http://xxxxx.net/json/'+ inputString,
dataType: 'json',
type: 'get',
});
apiRequest.done(function(data){
var lat = data['latitude'];
var lng = data['longitude'];
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(lat, lng),
zoom: 8
};
map = new google.maps.Map(document.getElementById('mapCanvas'), mapOptions);
}
// from GMaps API docs
function loadScript() {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp' + '&callback=initialize';
document.body.appendChild(script);
}
window.onload = loadScript;
}); // ends .done statement
} // ends findLocation
It should be that when you click the Find Location button it will generate a map in the mapCanvas div but it does not.
I found out from the Google Maps API documentation that if you want to asynchronously load a map you have to use that loadScript function.
And yes, the map divs have widths and heights in the CSS, so that's not the problem. I think the problem is with the Javascript scopes. I'm doing something wrong with the order or placement of the calls, just not sure what.
First, it is not true that you have to load the Maps API asynchronously when you want to create a map dynamically. You can load the API either using the asynchronous method or with a conventional <script> tag. Loading the API does not create a map; it merely loads the code. A map is not created until you call new google.maps.Map().
So one easy solution would be to go back to a <script> tag to load the API, and simply call your initialize() function directly inside your ajax callback. (Or pull the code out of the initialize() function and just run it inline inside the callback.)
In fact, in your current code you are already loading the Maps API at page load time, with this line:
window.onload = loadScript;
That causes the loadScript() function to be called at page load time.
So you may as well just use a <script> tag to load the API, it's more or less the same thing you're doing now but simpler.
If you do want to load the Maps API dynamically, #sirfriday's comment is correct: your initialize function is not visible in the global scope, and it needs to be so the asynchronous API load can call it when ready.
You mentioned that "calling initialize didn't do it." - but you shouldn't be calling initialize() in this case, you should set window.initialize to be a reference to the function, e.g.:
window.initialize = initialize; // note no () so it doesn't call it here
Or change the function like this:
window.initialize = function() {
...
};
And also if you want to load the API dynamically, don't do it at onload time. Instead, call your loadScript() function inside your ajax callback. That will give you the right sequence of operations:
Page loads
User interacts with it and the ajax request starts
Ajax request completes
Maps API loads
Your initialize() function gets called
I've been dissecting the following code snippet, which is used to asynchronously load the Segment.io analytics wrapper script:
// Create a queue, but don't obliterate an existing one!
var analytics = analytics || [];
// Define a method that will asynchronously load analytics.js from our CDN.
analytics.load = function(apiKey) {
// Create an async script element for analytics.js.
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = ('https:' === document.location.protocol ? 'https://' : 'http://') +
'd2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/' + apiKey + '/analytics.min.js';
// Find the first script element on the page and insert our script next to it.
var firstScript = document.getElementsByTagName('script')[0];
firstScript.parentNode.insertBefore(script, firstScript);
// Define a factory that generates wrapper methods to push arrays of
// arguments onto our `analytics` queue, where the first element of the arrays
// is always the name of the analytics.js method itself (eg. `track`).
var methodFactory = function (type) {
return function () {
analytics.push([type].concat(Array.prototype.slice.call(arguments, 0)));
};
};
// Loop through analytics.js' methods and generate a wrapper method for each.
var methods = ['identify', 'track', 'trackLink', 'trackForm', 'trackClick',
'trackSubmit', 'pageview', 'ab', 'alias', 'ready'];
for (var i = 0; i < methods.length; i++) {
analytics[methods[i]] = methodFactory(methods[i]);
}
};
// Load analytics.js with your API key, which will automatically load all of the
// analytics integrations you've turned on for your account. Boosh!
analytics.load('MYAPIKEY');
It's well commented and I can see what it's doing, but I'm puzzled when it comes to the methodFactory function, which pushes details (method name and arguments) of any method calls made before the main analytics.js script has loaded onto the global analytics array.
This is all well and good, but then if/when the main script does load, it seemingly just overwrites the global analytics variable (see last line here), so all that data will be lost.
I see how this prevents script errors in a web page by stubbing out methods which don't exist yet, but I don't understand why the stubs can't just return an empty function:
var methods = ['identify', 'track', 'trackLink', 'trackForm', 'trackClick',
'trackSubmit', 'pageview', 'ab', 'alias', 'ready'];
for (var i = 0; i < methods.length; i++) {
lib[methods[i]] = function () { };
}
What am I missing? Please, help me understand!
Ian here, co-founder at Segment.io—I didn't actually write that code, Calvin did, but I can fill you in on what it's doing.
You're right, the methodFactory is stubbing out the methods so that they are available before the script loads, which means people can call analytics.track without wrapping those calls in an if or ready() call.
But the methods are actually better than "dumb" stubs, in that they save the method that was called, so we can replay the actions later. That's this part:
analytics.push([type].concat(Array.prototype.slice.call(arguments, 0)));
To make that more readable:
var methodFactory = function (method) {
return function () {
var args = Array.prototype.slice.call(arguments, 0);
var newArgs = [method].concat(args);
analytics.push(newArgs);
};
};
It tacks on the name of the method that was called, which means if I analytics.identify('userId'), our queue actually gets an array that looks like:
['identify', 'userId']
Then, when our library loads in, it unloads all of the queued calls and replays them into the real methods (that are now available) so that all of the data recorded before load is still preserved. That's the key part, because we don't want to just throw away any calls that happen before our library has the chance to load. That looks like this:
// Loop through the interim analytics queue and reapply the calls to their
// proper analytics.js method.
while (window.analytics.length > 0) {
var item = window.analytics.shift();
var method = item.shift();
if (analytics[method]) analytics[method].apply(analytics, item);
}
analytics is a local variable at that point, and after we're done replaying, we replace the global with the local analytics (which is the real deal).
Hope that makes sense. We're actually going to have a series on our blog about all the little tricks for 3rd-party Javascript, so you might dig that soon!
Not very related to the question, but may be useful to those who googled for issue "segment not sends queued events".
In my code I assigned window.analytics to another variable at page loading stage:
let CLIENT = analytics;
Then I used this variable instead of using global analytics:
CLIENT.track();
CLIENT.page();
// etc
But I encountered a problem when sometimes events are sent, and sometimes nothing is being sent. That "sometimes" vary between page reloads. Sometimes it also could ignore all events that fire at page loading, and without page reloading start sending events that are binded after page loading.
Then I debugged and found that CLIENT holds all not sent events in queue. Obviously they were put using methodFactory(). Then I found this SO question. So that's what is happening I think:
CLIENT holds reference to stub analytics object, which calls this methodFactory(). After Segment is fully loaded it replaces window.analytics with actual code while CLIENT still holds reference to old window.analytics. That's why this "sometimes" happens: sometimes window.analytics was replaced by Segment before loading the main script which initializes this CLIENT, and sometimes main script loaded earlier than Segment script.
New code:
let CLIENT = undefined;
if (CLIENT) {
CLIENT.page();
} else {
window.analytics.page();
}
I need to have this CLIENT because I'm using same analytics code for web and mobile. On mobile this CLIENT will be initialized separately while on web window.analytics is always available.
I'm working on a implementing a Google map on a website with our own tiles overlays and KML elements. I've been previously requested to create code so that, for instance, when the page is loaded from a specific URL, it would initialize with one of the tile overlays already enabled. Recently, I've been requested to do the same for the buildings which are outlined by KML elements so that, arriving at the page with a specific URL, it would automatically zoom, center, and display information on the building.
However, while starting with the tile overlays work, the building KML does not. After doing some testing, I've determined that when the code which checks the URL executes, the page is still loading the KML elements and thus do not exist for the code to compare to or use:
Code for evaluating URL (placed at the end of onLoad="initialize()")
function urlClick() {
var currentURL = window.location.href; //Retrieve page URL
var URLpiece = currentURL.slice(-6); //pull the last 6 digits (for testing)
if (URLpiece === "access") { //If the resulting string is "access":
access_click(); //Display accessibility overlay
} else if (URLpiece === "middle") { //Else if the string is "middle":
facetClick('Middle College'); //Click on building "Middle College"
};
};
facetClick();
function facetClick(name) { //Convert building name to building ID.
for (var i = 0; i < active.placemarks.length; i++) {
if (active.placemarks[i].name === name) {
sideClick(i) //Click building whose id matches "Middle College"
};
};
};
Firebug Console Error
active is null
for (var i = 0; i < active.placemarks.length; i++) {
active.placemarks is which KML elements are loaded on the page, and being null, means no KML has been loaded yet. In short, I have a mistiming and I can't seem to find a suitable place to place the URL code to execute after the KMl has loaded. As noted above, I placed it at the end of onLoad="initialize()", but it would appear that, instead of waiting for the KML to completely load earlier in the function, the remainder of the function is executed:
onLoad="initialize()"
information(); //Use the buttons variables inital state to set up description
buttons(); //and button state
button_hover(0); //and button description to neutral.
//Create and arrange the Google Map.
//Create basic tile overlays.
//Set up parser to work with KML elements.
myParser = new geoXML3.parser({ //Parser: Takes KML and converts to JS.
map: map, //Applies parsed KML to the map
singleInfoWindow: true,
afterParse: useTheData //Allows us to use the parsed KML in a function
});
myParser.parse(['/maps/kml/shapes.kml','/maps/kml/shapes_hidden.kml']);
google.maps.event.addListener(map, 'maptypeid_changed', function() {
autoOverlay();
});
//Create other tile overlays to appear over KML elements.
urlClick();
I suspect one my issues lies in using the geoxml3 parser (http://code.google.com/p/geoxml3/) which converts our KML files to Javascript. While the page has completed loading all of the elements, the map on the page is still loading, including the KML elements. I have also tried placing urlClick() in the parser itself in various places which appear to execute after all the shapes have been parsed, but I've had no success there either.
While I've been intending to strip out the parser, I would like to know if there is any way of executing the "urlClick" after the parser has returned the KML shapes. Ideally, I don't want to use an arbitrary means of defining a time to wait, such as "wait 3 seconds, and go", as my various browsers all load the page at different times; rather, I'm looking for some way to say "when the parser is done, execute" or "when the Google map is completely loaded, execute" or perhaps even "hold until the parser is complete before advancing to urlClick".
Edit: Here are links to the map with the basic form of the issue found above. Since I've been developing the next update to the map on a test server, facetClick() is not part of this live version and I instead use its output function sideClick(); however the error is still the same in this arrangement:
active is null
google.maps.event.trigger(active.gpolygons[poly],'click');
Map: http://www.beloit.edu/maps/
Map w/Accessibility: http://www.beloit.edu/maps/?access
Map w/Building Click: http://www.beloit.edu/maps/?middle
EDIT: Spent most of my day working on rebuilding the functionality of the parser in Javascript and, low and behold, without the parser it works just fine. I figure that is obvious as I have to define each shape individually before the code, rather than waiting for it to be passed along by the parser. It would seem the answer is "if you want unique URLs, drop the parser". >_<
I've come across a similar problem when dealing with waiting for markers and infoWindows to load before executing a function. I found a solution here ( How can I check whether Google Maps is fully loaded? see #Veseliq's answer) that using the google maps event listener function for checking when the map is 'idle', does the trick. I assume this solution would work for KML layers as well. Essentially what you will have to do is include the following at the end of your initialize function:
google.maps.event.addListenerOnce(map, 'idle', function(){
// do something only the first time the map is loaded
});
In the API reference ( https://developers.google.com/maps/documentation/javascript/reference ) it states that the 'idle' event "is fired when the map becomes idle after panning or zooming". However, it seems to hold true that it is also fires on initial page load after everything in the map_canvas has loaded. And by using the addListenerOnce call, you ensure that it is never executed again after the initial page load (meaning it won't fire after a zoom or a pan action).
Second option:
As I mentioned you can take the callback approach, I believe this will only call your urlClick function after completing the parsing. Here's how you should probably arrange your code to make it work:
function someFunction(callback){
myParser.parse(['/maps/kml/shapes.kml','/maps/kml/shapes_hidden.kml']);
callback();
}
and then in your initialize you will have:
someFunction(function(){
urlClick();
});
You will have to make your map and myParser variables global.
Resources: This link had an excellent and detailed brief on how callback functions work in javascript, http://www.impressivewebs.com/callback-functions-javascript/