The Google Maps API allows all markers to be connected to a single infowindow

I've been grappling with this issue for some time now, but I just can't seem to find a solution! I'm retrieving data from a database in Laravel using Ajax and then attempting to display infowindows for each marker on Google Maps. The markers are correctly placed at different addresses, but they all share the same infowindow (which displays information from the last row in the database).

I tried incorporating this suggested solution into my code: Google Maps API v3 - Markers All Share The Same InfoWindow

However, it didn't work as expected...

This is an excerpt from my code:

var app = new Vue({
  el: 'body',

  data: {
    users: $.getJSON("http://localhost:8000/data", function(data){
              // Map initialization
              var map = new google.maps.Map(document.querySelector('#map'), {
                  center: {lat: 57.708870, lng: 11.974560 },
                  zoom: 14
              });

              // Geocoder setup
              var geocoder = new google.maps.Geocoder();

              // Function to bind infowindow to marker
              function bindInfoWindow(marker, map, infowindow, html) {
                  marker.addListener('click', function() {
                      infowindow.setContent(html);
                      infowindow.open(map, this);
                  });
              }

              for (var i = 0; i < data.length; i++) {

                // Setting address for geocoding
                var address = data[i]['address'] + ' Göteborg';
                // Generating HTML content for infowindow
                var contentString = '<h4 style="color: #ffc62d">' + data[i]['foodtruck_name'] + '</h4>'
                                    + '<b>Food:</b> ' + data[i]['type_of_food']
                                    + '<br><b>Opening Hours:</b> '+ data[i]['open_hours']
                                    + '<br><b>Address:</b> '+ data[i]['address']
                                    + '<br><b>Website:</b> '+ '<a href="http://' + data[i]['webadress'] + '" target="_blank">' + data[i]['webadress'] + '</a>';

                // Marker icon settings
                var image = {
                      url: 'http://localhost:8000/img/foodtruck.png',
                      size: new google.maps.Size(45, 30),
                      origin: new google.maps.Point(0, 0),
                      anchor: new google.maps.Point(0, 30)
                    };

                var shape = {
                      coords: [1, 1, 1, 30, 45, 20, 18, 1],
                      type: 'poly'
                    };

                var infoWindow = new google.maps.InfoWindow({
                  maxWidth: 250
                });

                // Asynchronous geocoding call
                geocoder.geocode({'address': address}, function(results, status) {
                if (status === google.maps.GeocoderStatus.OK) {
                  
                  // Creating marker based on geocoded location
                  var marker = new google.maps.Marker({
                    map: map,
                    position: results[0].geometry.location,
                    icon: image,
                    shape: shape
                  });

                } else {
                  alert('Geocode was not successful for the following reason: ' + status);
                }
                
                // Binding infowindow to each marker
                bindInfoWindow(marker, map, infoWindow, contentString);

              });

            };

          })
  },

  methods: {
    createMap: function() {
      // Alternate method for map creation
      var map = new google.maps.Map(document.querySelector('#map'), {
          center: {lat: 57.708870, lng: 11.974560 },
          zoom: 14
      });
    }

  }

});

If anyone has any insights or examples on how to resolve this issue, please share them with me! It's really frustrating! :-(

Here's the JSON data returned by the data object:

[{"foodtruck_name":"Emils Foodtruck","open_hours":"11:00-16:00","address":"Stigbergsliden 9","type_of_food":"Mexican cuisine","webadress":"www.emilwallgren.se"},{"foodtruck_name":"Kodameras Truck","open_hours":"08:00-17:00","address":"F\u00f6rsta L\u00e5nggatan 16","type_of_food":"Cookies","webadress":"www.kodamera.se"}]

Answer №1

To begin, let's kick things off by mocking the provided data. For this purpose, I will be overwriting $.getJSON.

var $ = {
  getJSON : function(url, callback){
    callback(
      [{"foodtruck_name":"Emils Foodtruck","open_hours":"11:00-16:00","address":"Stigbergsliden 9","type_of_food":"Mexikanskt tema","webadress":"www.emilwallgren.se"},{"foodtruck_name":"Kodameras Truck","open_hours":"08:00-17:00","address":"F\u00f6rsta L\u00e5nggatan 16","type_of_food":"Cookies","webadress":"www.kodamera.se"}]
    );
  }
};

Next, let's create a simple index.html to visualize the results and include the necessary frameworks from their CDN URLs.

<!doctype html>
<html class="no-js" lang="">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title>Maps</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <style type="text/css">
            #map{
                width: 100%;
                height: 300px;
            }
        </style>
    </head>
    <body>
        <div id = "map"></div>
        <script src="//cdn.jsdelivr.net/vue/1.0.25/vue.min.js"></script>
        <script src="js/main.js"></script>
        <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCTlirlFFxIHhstDEeARWQTym5AeU4db1I&callback=initMap"></script>
    </body>
</html>

It would have been great if this information was included in the question itself :-)

The main issue with the original code is that the geocoder.geocode method is asynchronous, while the bindInfoWindow function is called within the OK status condition, which may execute long after the for loop has finished. This results in passing arguments from the last iteration of the loop only.

To confirm this assumption, just insert geocoder.geocode({'address': address}, function(results, status) { console.log("outside"); if (status === google.maps.GeocoderStatus.OK) { console.log("inside"); } }); into your code, and you will see "outside" being triggered twice before "inside". The delay in "inside" execution is due to pending requests to Google.

A quick fix involves aggregating all data in the first loop and utilizing a recursive function.

The complete code block appears as follows (wrapped within the default maps callback):

var $ = {
  getJSON : function(url, callback){
    callback(
      [{"foodtruck_name":"Emils Foodtruck","open_hours":"11:00-16:00","address":"Stigbergsliden 9","type_of_food":"Mexikanskt tema","webadress":"www.emilwallgren.se"},{"foodtruck_name":"Kodameras Truck","open_hours":"08:00-17:00","address":"F\u00f6rsta L\u00e5nggatan 16","type_of_food":"Cookies","webadress":"www.kodamera.se"}]
    );
  }
};
initMap = function(){
  var app = new Vue({
    el: 'body',
    data: {
        users: $.getJSON("http://localhost:8000/data", function(data){
            function bindInfoWindow(marker) {
                marker.addListener('click', function() {
                    this.infowindow.setContent(this.infowindowContent)                    
                    this.infowindow.open(this.map, this);
                });
            }
            var addmarkersRecursive = function addMarkers(markers){
                if(markers.length > 0){
                    markerConfig = markers.shift();
                    geocoder.geocode({'address': markerConfig.address}, function(results, status) {
                        if (status === google.maps.GeocoderStatus.OK) {
                            var marker = new google.maps.Marker({
                                map: markerConfig.map,
                                position: results[0].geometry.location,
                                icon: markerConfig.image,
                                shape: markerConfig.shape,
                                infowindow : markerConfig.infowindow,
                                infowindowContent : markerConfig.contentString

                            });
                            bindInfoWindow(marker, markerConfig.map, markerConfig.infoWindow, markerConfig.contentString);
                            addmarkersRecursive(markers);                            
                        } else {
                            alert('Geocode was not successful for the following reason: ' + status);
                        }
                    });
                }
            };
            var map = new google.maps.Map(document.querySelector('#map'), {
                center: {lat: 57.708870, lng: 11.974560 },
                zoom: 14
            });
            var geocoder = new google.maps.Geocoder();
            var markers = [];
            for (var i = 0; i < data.length; i++) {
                var address = data[i]['address'] + ' Göteborg';
                var contentString = '<h4 style="color: #ffc62d">' + data[i]['foodtruck_name'] + '</h4>'
                + '<b>Mat:</b> ' + data[i]['type_of_food']
                + '<br><b>Öppettider:</b> '+ data[i]['open_hours']
                + '<br><b>Adress:</b> '+ data[i]['address']
                + '<br><b>Hemsida:</b> '+ '<a href="http://' + data[i]['webadress'] + '" target="_blank">' + data[i]['webadress'] + '</a>';
                var image = {
                    url: 'http://t1.gstatic.com/images?q=tbn:ANd9GcT_vg5Yh1dmbqL4cVfaBoZhFfPwXJIZhJ5MFU9Y6lm4173JsKb8XEFK',
                    size: new google.maps.Size(45, 30),
                    origin: new google.maps.Point(0, 0),
                    anchor: new google.maps.Point(0, 30)
                };
                markers.push({
                    map : map,
                    address: address,
                    contentString: contentString,
                    image: image,
                    shape : {
                        coords: [1, 1, 1, 30, 45, 20, 18, 1],
                        type: 'poly'
                    },
                    infowindow : new google.maps.InfoWindow({maxWidth: 250 })
                })
            };
            addmarkersRecursive(markers);
        })
    },

    methods: {
      createMap: function() {
        var map = new google.maps.Map(document.querySelector('#map'), {
            center: {lat: 57.708870, lng: 11.974560 },
            zoom: 14
        });
      }

    }

  });
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way for me to retrieve the value from an input, round it up to the nearest even number, and assign it to a new variable?

I am working on a project that involves two text boxes and a drop-down menu for providing rates. The values in the text boxes are given as inches. I want these inputs to be taken as values, rounded up to the next even number, and then set as variables. How ...

Issue with mouse hover not triggering overlay effect between two div elements

I am trying to display articles in a 2x3 matrix with article details like image, title, author, and description wrapped inside a div. I want this entire div to be overlapped by another div of the same dimensions containing a single image. Below is a snipp ...

Keeping calculated values in the React state can cause issues

In an attempt to develop a component resembling a transferlist, I have simplified the process substantially for this particular issue. Consider the following example: the react-admin component receives two inputs - a subset of selected items record[source ...

Strangely peculiar glitch found in browsers built on the Chromium platform

During a school assignment, I'm attempting to adjust the width of a button using Javascript. Below is my code: const button = document.querySelector("button"); button.addEventListener("click", () => { console.log(button.offsetWidth); butto ...

With Vue 3 Pinia, values fetched from an API within a Pinia store cannot be statically assigned; they will always be reactive

I have a frontend built with vue3 and using pinia as the store. I am setting it up as shown below: export const useShopStore = defineStore( "shop", () => { const state = reactive({ data: { fruits: [], owners: [] ...

What is the solution to the error message "A TypeError was encountered in the current module: e.parse is not a function"?

Can someone please help me resolve this Vue Js error I am encountering in Shopware 6 Administration? The issue pertains to selecting a column in the database table using a module. Note: Below is the complete code snippet. I am attempting to retrieve data ...

Executing an AJAX request to insert data into MySQL

After my extensive online search, I came across a solution that involves using ajax to set information in my database. While browsing through this page, I learned how to retrieve data from the database. My specific query is: can we also use ajax to set in ...

Creating dynamic values in data-tables using Vuetify

As I work with JSON data, my current task involves formatting it using Vuetify's Data Tables. The official documentation provides guidance on defining table headers as shown below: import data from './data.json' export default { data ...

Apple Automation: Extract a targeted string from text and transfer it to a different spot within the page

Apologies for my lack of expertise in this area, but I hope to convey my question clearly. I am working on a form where I need to input text in order to copy specific items and paste them elsewhere on the same webpage. For example, if the input text is " ...

Is the 'wait > remaining' condition ever satisfied in the throttle function of underscore.js?

Check out the library code at line 860: https://github.com/jashkenas/underscore/blob/master/underscore.js if (remaining <= 0 || remaining > wait) Under what circumstance would the second part of this statement be true? Background - This is my firs ...

AngularJS - development of a service entity

After deliberating between posting in the Angular mailing list or seeking assistance from the JavaScript community, I have decided that this is more of a JavaScript question. Hopefully, the knowledgeable individuals on Stack Overflow can provide a quicker ...

What is the best way to incorporate several php files into a WordPress post?

I have developed a system that retrieves information from a database and presents it in multiple text files. Now, I am looking to move this system to a page on a WordPress website. I have already created a custom page named page-slug.php and added the ne ...

Using AngularJS to encapsulate an externally loaded asynchronous library as a service

Is there a way to wrap a 3rd party library that loads asynchronously into an Angular service? What is the best practice for incorporating such libraries as services in Angular? Currently, I am approaching it like this: angular.module('myAPIServices ...

Animating Array of Paragraphs with JQuery: Step-by-Step Guide to Displaying Paragraph Tags Sequentially on Each Click

var phrases = ['phraseone', 'yet another phrase', 'once more with feeling']; $(".btn").on('click', function() { for(var i=0; i < phrases.length; i++) { container.innerHTML += '<p>' + ...

What is the correct placement for $.validator.setDefaults({ onkeyup: false }) in order to deactivate MVC3 onKeyup for the Remote attribute?

After coming across various solutions on how to disable the onKeyup feature of MVC3 Remote Validator, I noticed that many suggest using the following code: $.validator.setDefaults({ onkeyup: false }); However, I'm in a dilemma about where to place t ...

Is it possible to use both the .load function and fadeIn in jQuery simultaneously?

Currently, I am utilizing the .load method to bring in another page using jQuery. Below is the code snippet: $('#page1').click(function(){ $("#content").load("page1.html"); }); While this code works well, I am inte ...

What is the best way to convert my Chatbot component into a <script> tag for seamless integration into any website using React.js?

I have successfully integrated a Chatbot component into my Next.js application. https://i.stack.imgur.com/BxgWV.png Now, I want to make this component available for anyone to use on their own website by simply adding a tag. My initial approach was to cre ...

Incorporating an external HTML page's <title> tag into a different HTML page using jQuery

I am faced with a challenge involving two files: index.html and index2.html. Both of these files reside in the same directory on a local machine, without access to PHP or other server-side languages. My goal is to extract the <title>Page Title</ ...

Encountering a connection error while running a Node.js Selenium test case on Chrome. The specific error message received is: "Error: ECONNREFUSED - Connection refused to 127.0

When attempting to run a test case on a Selenium Node.js server, an error was encountered: Error: ECONNREFUSED connect ECONNREFUSED. The test case script is as follows: var assert = require('assert'), test = require('selenium-webdriver ...

I am unable to implement v-bind click within a v-for loop

While working with VueJS framework v-for, I encountered a problem when trying to loop through lists of items. Each item index was supposed to be assigned to a variable, but the v-bind click event wasn't being attached properly inside the v-for element ...