Vue / Google Maps infoWindow include click

Viewed 965

I've implemented the Google Maps API in a Vue project, I would've used a library like vue2-google-maps but a college of my told me it was better to use the plain JavaScript API as we might have a lot of markers and in his experience the library really struggled when there were to many markers.

So now I've created this method to render the Google Map - Map on the page using Vue:

methods: {
  async generateMap() {
    // Start and await Google script
    const google = await gmapsInit();

    // Create Google Map object
    this.map = new google.maps.Map(this.$el, {
      zoom: 7,
      center: new google.maps.LatLng(52.100886023504415, 5.6446197918489)
    });

    // Standplaats is an object containg a name, lat, lon and slug
    this.standplaatsen.forEach(standplaats => {
      const position = {
        lat: standplaats.location.latitude,
        lng: standplaats.location.longitude
      };

      // Create the marker and add to map
      const marker = new google.maps.Marker({
        position: position,
        title: standplaats.name,
        map: this.map,
      });

      marker.addListener("click", () => {
        const infoWindow = new google.maps.InfoWindow({
          content:
            `<div>` +
              `<h4>${standplaats.name}</h4>` +
              `${standplaats.location.street}<br/>` +
              `${standplaats.location.zipcode} ${standplaats.location.location}<br/>` +
              `<hr/>` +
              `<a>${standplaats.posts} opdrachten beschikbaar</a>` +
            `</div>`
        });

        infoWindow.open(this.map, marker);
      });
    });
  },
}

The infoWindow contains an hyperlink (<a>${standplaats.posts} opdrachten beschikbaar</a>), I want to trigger an Vue emit event when this hyperlink is clicked. Since this HTML is being render through the Google API I can't just include an @click event.

The solution I thought of was to include an unique ID to the hyperlink element and then adding an eventListeren directly afterwards, so something like this

marker.addListener("click", () => {
  const infoWindow = new google.maps.InfoWindow({
    content:
      `<div>` +
        `<h4>${standplaats.name}</h4>` +
        `${standplaats.location.street}<br/>` +
        `${standplaats.location.zipcode} ${standplaats.location.location}<br/>` +
        `<hr/>` +
        `<a id="${standplaats.slug}">${standplaats.posts} opdrachten beschikbaar</a>` +
      `</div>`
  });

  infoWindow.open(this.map, marker);

  document.getElementById(`${standplaats.slug}`).addEventListener("click", () => {
    console.log('I\'ve been clicked');
  });
});

But this doesn't work since it takes a while for the Google API to render the element, the only way I could solve this is wrapping the eventListerener in a 1 second timeout but this whole thing is starting to feel pretty bad practice right about now...

Any suggestions on how to handle this?

1 Answers

I would suggest 3 things:

  1. Check Google API Docs if there is any Callback you can pass, or if clicking content does emit some listeners. That would be the best.
  2. Just to make it working you can add listener to document and check click event.target if it matches your element with id. Something like this:
 document.addEventListener("click", (event) => {
  if (event.target.id && event.target.id === standplaats.slug) {
    console.log('I\'ve been clicked');
  }
});
  1. You can try to put a Vue Component as content, and add @click there. But I'm not sure if this is going to work. Just flew to my mind
Related