javascriptgoogle-mapsreactjsreduxinfobox

Rendering React Component into GoogleMaps InfoBox - Stop click throughs


I'm having an issue with google maps InfoBox in a React application. My goal is to open up an empty InfoBox on a google maps instance, and then use ReactDOM.render to put my component inside of it. The component comprises of a set of tabs, with each tab pane making an API call through redux and thunk when it's opened.

This all works fine, it displays nicely. The problem comes from actually clicking on the InfoBox. I can get it to work in one of two ways by changing the enableEventPropagation to be true or false:

  1. enableEventPropagation = true: The component works, all the tabs and buttons, but any markers underneath my InfoBox will be opened if I happen to click on the right part of the InfoBox, which completely breaks the state and flow of my application.

  2. enableEventPropagation = false: The clicks don't go through to the map, but then none of the click events in the react component work. No tabs, no buttons: nothing. Looks great but doesn't work at all.

Here's my code for creating my InfoBox and rendering to it with ReactDOM:

renderInfoBox() {
    let {map, google, infoBox} = this.props;
    let markerElement = MarkerElementService.getMarkerElement(infoBox.ui.markerId);

    this.infoBox = new this.infoBoxClass({
        content: this.refs.infoBoxHolder,
        enableEventPropagation: false,
        pixelOffset: new google.maps.Size(10, -275),
        maxWidth: 350,
        zIndex: 99999999,
        infoBoxClearance: new google.maps.Size(75, 75)
    });

    ReactDOM.render(
        <Provider store={AppStore}>
            <ThemeProvider theme={Theme}>
                <InfoBoxComponent google={google} onClose={this.closeButtonClicked.bind(this)} />
            </ThemeProvider>
        </Provider>,
        this.refs.infoBoxHolder,
        () => {
            this.infoBox.open(map, markerElement);
        }
    );
}

render() {
    return (
        <div ref="infoBoxHolder" />
    );
}

I have tried the following solutions:

It seems my only option left is to somehow calculate the Lat/Lng bounds of the InfoBox once it's been rendered and then sequentially disable all markers that are in that area, but that's a hacky load of nonsense I want to avoid!

Or maybe I have completely the wrong approach?


Solution

  • There is a few things you could try.

    First, if you don't have a lot of markers, you can definitely try to set your markers optimized flag to false. It will render them as separate DOM elements, decreasing the performance but you won't get weird behaviors like you are experiencing.

    I couldn't reproduce your issue with normal markers, but was able to with the POIs ones that are added by default. To prevent openings of any marker behind your InfoBox, you can create a listener on enter and leave on your InfoBox that will set a boolean, and override the set method of InfoWindow to cancel default behavior while you are hovering. You still can see the mouse cursor changing but that can be removed using some basic CSS.

    Here is an example, try clicking on the Sechelt Aquatic Center marker after clicking on the marker to open the InfoxBox, you shouldn't be able to do it.

    function initialize () {
    
      let enableMarkers = true
      
      const set = google.maps.InfoWindow.prototype.set;
      google.maps.InfoWindow.prototype.set = function (key, val) {
        if (key === 'map') {
          if (!enableMarkers) {
            return
          }
        }
        set.apply(this, arguments)
      }
    
      const map = new google.maps.Map(document.getElementById('map'), {
        zoom: 15,
        center: new google.maps.LatLng(49.47216, -123.76307),
        mapTypeId: google.maps.MapTypeId.ROADMAP,
      })
    
      const marker = new google.maps.Marker({
        map,
        draggable: true,
        position: new google.maps.LatLng(49.47216, -123.76307),
        visible: true
      })
    
      const box = document.createElement('div')
      box.innerHTML = 'Hello <br> Hello <br> Hello'
    
      google.maps.event.addListener(marker, 'click', function (e) {
        if (!enableMarkers) { return }
        ib.open(map, this)
      })
    
      const ib = new InfoBox({
        content: box,
        zIndex: 10,
        pixelOffset: new google.maps.Size(-100, 0),
        boxStyle: { 
          background: 'white',
          padding: '5px',
          opacity: 0.75,
          width: '200px',
        },
        closeBoxMargin: '2px',
        closeBoxURL: 'https://www.google.com/intl/en_us/mapfiles/close.gif',
        isHidden: false,
        enableEventPropagation: true,
      })
      
      box.addEventListener('mouseenter', () => {
        enableMarkers = false
      })
      
      box.addEventListener('mouseleave', () => {
        enableMarkers = true
      })
    }
    <script src="https://maps.googleapis.com/maps/api/js?v=3&amp;sensor=false"></script>
    <script src="https://rawgit.com/googlemaps/v3-utility-library/master/infobox/src/infobox.js"></script>
    
    <body onload="initialize()">
      <div id="map" style="width: 100%; height: 200px"></div>
    </body>

    I also saw in a few places where people had the same kind of issue and had to wait for the InfoBox domready event to start binding their clicks. Don't really think that's relevant since I don't really know how you could do that using React, but still worth noting.