75

I am following this tutorial, basically copy all the code

https://developers.google.com/maps/documentation/javascript/tutorial

but got an error saying that the initMap function is not a function. I am using angularjs in my project, could that be causing problems?

I copied the same code into plunker and it works just fine... What are the possible issues?

XoXo
  • 1,560
  • 1
  • 16
  • 35
user2901633
  • 989
  • 1
  • 8
  • 15

22 Answers22

68

Actually the error is being generated by the initMap in the Google's api script

 <script async defer
  src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>

so basically when the Google Map API is loaded then the initMap function is executed. If you don't have an initMap function, then the initMap is not a function error is generated.

So basically what you have to do is one of the following options:

  1. to create an initMap function
  2. replace the callback function with one of your own that created for the same purpose but named it something else
  3. replace the &callback=angular.noop if you just want an empty function() (only if you use angular)
DB.Null
  • 1,134
  • 7
  • 11
  • 17
    Eventough function initMap exists, it keeps saying the same thing – Add080bbA Apr 10 '16 at 23:10
  • 5
    is your initMap function loaded prior the callback event? Could you provide the error or how you define the function? Hint: this function must be global, or if it is a property of an object, you have to update the callback function in the gmap link to point to this specific property and make sure that the object is instantiated before the callback event. – DB.Null Apr 12 '16 at 15:53
  • Why did i get a negative score because of this comment ? Problem was because of wordpress' wysiwyg html editor. It was inserting random

    tags for the empty lines in js code inserted. i made the js code one line, problem gone.

    – Add080bbA Apr 12 '16 at 18:36
  • 2
    (first one solve. thanks)--> window.initMap = function() {}; – Sergio Cabral Apr 15 '16 at 02:30
  • 4
    I was having the same issue as OP, and i was following the same tutorial as well as a few others. My problem was it was randomly occuring, once every 10 times i loaded it, InitMap was not a function. Your comment actually made me notice that the JS was called after the Map API, so it would make sense that the occasionally the API would finish before the JS and then call the function in the JS which was not loaded yet, hence Not existing (not a function). Moving the JS above the API call and 50 reloads later, still no error. – Ryan May 03 '16 at 19:43
  • This API call must happen after the file in which `initMap` has loaded. So in your script list in your html file, put this below the file in which you placed `initMap`. – Max Phillips Nov 11 '16 at 23:10
  • is this function name fixed? i have added callback=InitializeMap instead, but its not working – Samra Aug 29 '18 at 06:50
  • It looks like without the following code "window.initMap = initMap;" It works fine. Can anybody explain do I need that line? – RotatingWheel Jun 06 '22 at 10:34
54

My explanation of this problem:

The .....&callback=initMap" async defer></script> makes that script load async (in parallel with DOM) and after load - execute initMap function. NOTICE that initMap() in global scope! We can't declare our initMap() after google's script because it should already be at the moment of finish async load. Although we can't load our script before this, because we need google's function to execute ours. This is like vicious circle.

Three solutions:

1'st and worst: make google's script load synchronously

  1. remove async defer
  2. remove &callback=initMap in src attribute
  3. put <script tag with your code after google's script

2'nd and best: just do this =)

  1. leave .....&callback=initMap" async defer></script> as is
  2. and put google's <script tag after your script
  3. write in your script

    function initMap() {} // now it IS a function and it is in global
    
    $(() => {
      initMap = function() {
        // your code like...
        var map = new google.maps.Map(document.getElementById('map'), {/*your code*/});
        // and other stuff...
      }
    })
    

    this allow you load google's script async and run yours just after that

3'rd and strange: it will work... some times =)

  1. do same but simply write in global scope

    function initMap() {
      // your code
    }
    

    and if you write it in global scope, that will work regardless of what which code loads faster, your (sync) or google's (async). More often your wins

Andrey Kudriavtsev
  • 1,124
  • 11
  • 19
  • 1
    Thanks for your answer. It worked! Can you please elaborate your second option? I'm new to JQuery. How does $(() => { }), make sure the method runs after the maps is loaded ? Is it like a shorthand for document.ready? – Ranjith V Jun 25 '17 at 06:37
  • I've added #2 verbatim, and I'm receiving this error: `Uncaught SyntaxError: Unexpected token }`. From this line: `var map = new google.maps.Map(document.getElementById('map'), {...});` What's causing that error? – jeremyhoover Mar 02 '18 at 18:36
  • inside {...} you should put your code, not "..." I just wrote that to point that there should be some code. Sorry, I'll change it to comment. – Andrey Kudriavtsev Mar 05 '18 at 10:13
  • Why is the first solution worse? In my case, i don't want to call the map on page load. I want to show it when a specific element is clicked. And this element is loaded dynamically from WebSQL, therefore, i don't even know when the element will be loaded, and i'm pretty sure google script will load faster. In that case, the best solution should be to remove the callback, and call the map when my element is clicked, right? – Israel Obanijesu Oct 21 '19 at 06:00
  • @IsraelObanijesu , I would do things like -> user click element -> show loader -> load google map -> initialize google map -> hide loader. So I would not even load google map on page load whether sync or async. – Andrey Kudriavtsev Oct 26 '19 at 18:30
  • @AndreyKudriavtsev Please, can you show an example of that, the part of loading google map after showing the loader, i can't picture how that would be done. I had a recent idea which was to use the callback method you specified, load and initialize the map with random location data, but hide it, then when the element is clicked, display the map with appropriate location data. I'm trying to choose the most efficient method for my app. – Israel Obanijesu Oct 27 '19 at 01:45
16

I have been struggling for several days with this very popular in the last few months issue - "initMap is not a function".

Those two threads helped me:

  1. How to make a callback to Google Maps init in separate files of a web app

  2. Defer attribute doesn't work with Google Maps API?

Why does the map open sometimes and sometimes not. It depends on several factors like speed of connection, environment, etc. Because the initialization function sometimes runs after the google maps API kicks in, that's why the map is not displayed and the browser console throws an error. For me removing only the async attribute fixed the issue. The defer attribute stays.

If async is present: The script is executed asynchronously with the rest of the page (the script will be executed while the page continues the parsing) If async is not present and defer is present: The script is executed when the page has finished parsing If neither async or defer is present: The script is fetched and executed immediately, before the browser continues parsing the page Source - http://www.w3schools.com/tags/att_script_defer.asp

Hope that helps. Cheers.

Community
  • 1
  • 1
Viktor Georgiev
  • 239
  • 1
  • 3
  • 7
15

Removing =initMap worked for me:

<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback"></script>
Kick Buttowski
  • 6,709
  • 13
  • 37
  • 58
Tom
  • 151
  • 1
  • 2
  • 5
    I think if initMap will be omitted, the map will not load in the first load? – Kick Buttowski Aug 28 '17 at 17:11
  • 1
    How did you create the map or called your callback respectively? – dakab Apr 12 '18 at 07:34
  • This works perfectly with Angular since the problem is just the error and everything else works without any initMap function – Jason Simpson May 28 '20 at 00:53
  • @KickButtowski If the initMap function is called on a component (in Angular) then the callback isn't needed in the script tag in index.html. The error appears when the application loads and doesn't find initMap(), but on navigating to a route containing a map the typescript file calls initMap(), which it will do anyway to set coordinates an d options. My map was working (remember to specify a height in CSS!) but I was still getting this error as soon as the app started. Removing the callback removed the error, and the maps work as expected. – Rin and Len Feb 25 '22 at 11:13
  • Also using angular, I removed the initMap from the script in my index.html and create initMap at the component level. We have many maps across a large application and it makes more sense to call it when the user navigates to each page, and all the maps have different data etc. – Rin and Len Mar 03 '22 at 10:46
10

The problem has to do with the async attribute in the script tag. The Callback Function is trying to call "initMap()" when it doesn't really exists by the time the request finished.

To solve this I placed the Goole Maps Api Script bellow the script where my initMap function was declared.

Hope this helps

krlozadan
  • 317
  • 2
  • 5
  • 10
9

This may seem obvious but just in case: If someone placed the JS code inside $(document).ready like this:

     $(document).ready(function() {
       ... Google Maps JS code ...
      }

Then that's the problem because using async defer when loading the Google Maps API library, it will load asynchronously, and when it finishes loading, will look for the callback function, which needs to be available by then.

So, you just need to put the code outside $(document).ready, and:

<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
    async defer></script>

at the very bottom, so your page loads FAST :-)

Kick Buttowski
  • 6,709
  • 13
  • 37
  • 58
Hugo
  • 2,069
  • 22
  • 23
5

Solved by adding

<script async defer
    src="https://maps.googleapis.com/maps/api/js?key=XXXXXXX&callback=initMap">
    <!--
        https://developers.google.com/maps/documentation/javascript/examples/map-geolocation
    -->
</script>

At the beginning of the same file which contains the rest of the code with function initMap(). It's definitely not the best solution, but it works..

But I think that if you would transform function initMap() to something like var=initMap() and then $(function () ... it would work too.

Murray Foxcroft
  • 12,785
  • 7
  • 58
  • 86
vohratom
  • 422
  • 7
  • 16
  • What’s the actual point? Having an HTML comment inside the script element? Putting selfsame at the beginning? – dakab Apr 12 '18 at 07:32
4

I am using React and I had mentioned &callback=initMap in the script as below

<script 
src="https://maps.googleapis.com/maps/api/js? 
key=YOUR_API_KEY&callback=initMap">
</script>

then is just removed the &callback=initMap part now there is no such error as window.initMap is not a function in the console.

Samkit Shah
  • 268
  • 2
  • 7
4

your call back method probably is not globally accessible. in my case I'd used transpoiled ES6 codes via webpack which caused my callback method not being global anymore.

Try to attach your callback method explicitly to window like so right after your callback method declaration and see the result

window.initMap = initMap;

it worked for me.

Code_Worm
  • 4,069
  • 2
  • 30
  • 35
4

For me the main difference was the declaration of the function....

INSTEAD OF

function initMap() {
    ...
}

THIS WORKED

window.initMap = function () {
    ...
}
The Vojtisek
  • 1,282
  • 17
  • 21
3

Create initMap method between "" tag or load javascript file before call google api.

<script src="Scripts/main.js"></script>

<script src="https://maps.googleapis.com/maps/api/js?key=abcde&libraries=places&callback=initMap" async defer></script>
2

Put this in your html body (taken from the official angular.js website):

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>

I believe that you were using some older angular.js files since you don't have any issues in plunker and therefore you got the specified error.

EugenSunic
  • 13,162
  • 13
  • 64
  • 86
1

turns out it has to do with ng-Route and the order of loading script wrote a directive and put the API script on top of everything works.

Kick Buttowski
  • 6,709
  • 13
  • 37
  • 58
user2901633
  • 989
  • 1
  • 8
  • 15
1

I had a similar error. The answers here helped me figure out what to do.

index.html

 <!--The div element for the map -->
 <div id="map"></div>

<!--The link to external javascript file that has initMap() function-->
 <script src="main.js">

<!--Google api, this calls initMap() function-->
 <script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY&callback=initMap">
</script>

main.js // This gives error


// The initMap function has not been executed
const initMap = () => {
const mapDisplayElement = document.getElementById('map');
  // The address is Uluru
const address = {lat: -25.344, lng: 131.036};
  // The zoom property specifies the zoom level for the map. Zoom: 0 is the lowest zoom,and displays the entire earth.
const map = new google.maps.Map(mapDisplayElement, { zoom: 4, center: address });
const marker = new google.maps.Marker({ position: address, map });
};

The answers here helped me figure out a solution. I used an immediately invoked the function (IIFE ) to work around it.

The error is as at the time of calling the google maps api the initMap() function has not executed.

main.js // This works

const mapDisplayElement = document.getElementById('map');
// The address is Uluru
// Run the initMap() function imidiately, 
(initMap = () => {
  const address = {lat: -25.344, lng: 131.036};
  // The zoom property specifies the zoom level for the map. Zoom: 0 is the lowest zoom,and displays the entire earth.
  const map = new google.maps.Map(mapDisplayElement, { zoom: 4, center: address });
  const marker = new google.maps.Marker({ position: address, map });
})();
1

What actually worked for me was:

  1. In the HTML file, I removed the async attribute from the <script> tag.
  2. In the JavaScript file, I double-checked that the function was outside the $(document).ready function.
jackdbd
  • 4,583
  • 3
  • 26
  • 36
1

This is the top result when you Google 'initMap function is not a function' so although there are a lot of answers already, I will make it very clear for the rookies like me what is happening.

To load a Google Map your page need 3 things:

  1. A <div id="map"></div>
  2. Load the Google Map javascript.
  3. A function to initiate the map.

The div (easy)

The div is easy. The Google Map Javascript is easy but there is a catch.

Load Google Maps Javascript

<script
  src="https://maps.googleapis.com/maps/api/jskey=YOURKEY&callback=initMap"
  defer
></script>

There are two important parts of this script. defer, and the callback=initMap

First let's talk about defer. The defer attribute causes the callback to execute after the full HTML document has been parsed. This makes sure your div is loaded and ready to be used by the javascript. However, does not mean that the Google Maps Javascript will load after your Javascript, and that is why the error happens sometimes.

The init function

You need to call a function to run the code that sets up the Google Map. The callback makes it easy because once the Google Maps code loads, it will call a function you tell it to load the map.

I'll repeat that because it is important. The callback=initMap is calling the initMap function in your code.

So if your code hasn't loaded by the time the Google Maps javascript loads, it will call initMap but you get the error because it doesn't know what initMap is because your code, where you define initMap hasn't loaded yet.

This can happen seemingly randomly as network times vary. Sometimes you code loads first, and it works. Sometimes your code doesn't load first and Google Maps code loads first and you get the error.

Solution

There are a lot of solutions, but the one that I like is don't set a callback, and init the Map yourself after you know everything is loaded.

This way, we can load Google Maps Javascript async, which is faster since it loads in a separate process and doesn't wait for anything.

<script async
  src="https://maps.googleapis.com/maps/api/js?key=YOURKEY">
</script>

Notice how there is no callback set. So Google Map Javascript will not automatically call my init function, I have to call it when I know everything is ready.

We can do that with a window load event listener.

window.addEventListener("load", docReady);

function docReady() {
    initMap();
}

The window load event fires after everything including Javascript has loaded.

Of course this might slow the page down as it has to load everything, including images, and then it will fire and initialize the map, but it ensures it will work every time.

As you learn more, you will learn other ways to make sure things load in the correct order. Good luck!

Joshua Dance
  • 8,847
  • 4
  • 67
  • 72
0

Could be your initMap function is in a $(document).ready function. If it is then it won't work, it has to be outside of any other functions.

KNey
  • 1
0

In addition to @DB.Null's answer, I used Function.prototype as no-op (no-operation) function on #3 instead of angular.noop (I don't have angular in my project).

So this...

<script async defer src="https://maps.googleapis.com/maps/api/js?key=API_KEY_HERE&callback=Function.prototype" type="text/javascript"></script>

0

In my case there was a formatting issue earlier on, so the error was a consequence of something else. My server was rendering the lat/lon values with commas instead of periods, because of different regional settings.

Savage
  • 2,296
  • 2
  • 30
  • 40
0

I removed the callback of "initMap" from the script tag and placed the callback in my JavaScript file.

<script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY&callback=initMap"></script>

was changed to

<script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY"></script>

So the first JS tag is mine.

<script async type=module src="js/app.js"></script> <script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY"></script>

I called the initMap function from within my JS file by adding the following to my app.js file.

initMap();

0

This solution helped me:

this.initMap = () => {};

...

src="https://maps.google.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
-1

In my case, I had to load the Map on my Wordpress website and the problem was that the Google's api script was loading before the initMap(). Therefore, I solved the problem with a delay:

<script>
function initMap() {
     // Your Javascript Codes for the map
     ...
}

<?php
// Delay for 5 seconds
sleep(5);
?>

</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY&callback=initMap"></script>