Search code examples
google-mapspopuptooltippositioning

Google Maps V3 tooltip popup with dynamic auto-positioning


I'm looking for a popup that will work with Google Maps V3 that supports auto-positioning the popup in relation to the marker such that the whole popup window is always visible within the map viewport. I'm trying to hack the InfoBox library to make this work but it's proving to be a big hassle. I've also looked at QTip2 which shows some promise but also has some shortcomings such as having positioning, but it must be set manually.

EDIT: The solution needs to to not pan the map to show the popup window.


Solution

  • I was able to add on to InfoBox to get it to do what I wanted with a little help from SmartInfoWindow. This is a partially customized solution so you may need to tweak the positioning. You just grab the div position of each corner of the InfoBox, convert those corners back to lat/lng and then grab the maps bounds and see if the corners are within the bounds. If they aren't then you adjust the InfoBox position accordingly depending on which corners are off the map.

    InfoBox.prototype.maybePanMap = function() {
      // if we go beyond map, pan map
      var map = this.getMap();
      var projection = this.getProjection();
      var bounds = map.getBounds();
      if (!bounds) return;
    
      // The dimension of the infowindow
      var iwWidth = this.div_.offsetWidth;
      var iwHeight = this.div_.offsetHeight;
    
      // The offset position of the infowindow
      var iwOffsetX = this.pixelOffset_.width;
      var iwOffsetY = this.pixelOffset_.height;
    
      var anchorPixel = projection.fromLatLngToDivPixel(this.getPosition());
      var bl = new google.maps.Point(anchorPixel.x + iwOffsetX,
          anchorPixel.y + iwOffsetY);
      var br = new google.maps.Point(anchorPixel.x + iwOffsetX + iwWidth,
          anchorPixel.y + iwOffsetY);
      var tl = new google.maps.Point(anchorPixel.x + iwOffsetX,
          anchorPixel.y + iwOffsetY - iwHeight + 100);
      var tr = new google.maps.Point(anchorPixel.x + iwOffsetX + iwWidth,
          anchorPixel.y + iwOffsetY - iwHeight + 100);
    
      var sw = projection.fromDivPixelToLatLng(bl);
      var se = projection.fromDivPixelToLatLng(br);
      var nw = projection.fromDivPixelToLatLng(tl);
      var ne = projection.fromDivPixelToLatLng(tr);
    
    
      if (!map.getBounds().contains(nw) && !map.getBounds().contains(sw)) {        
        this.div_.style.left = (anchorPixel.x + 10) + 'px';
        if (!map.getBounds().contains(ne)) {
          this.div_.style.top = (anchorPixel.y - 100) + 'px';
        }
      } else if (!map.getBounds().contains(nw) && !map.getBounds().contains(ne)) {
        this.div_.style.top = (anchorPixel.y - 100) + 'px';
        if (!map.getBounds().contains(se)) {
          this.div_.style.left = (anchorPixel.x - iwWidth - 10) + 'px';
        }
      } else if (!map.getBounds().contains(ne) && !map.getBounds().contains(se)) {
        this.div_.style.left = (anchorPixel.x - iwWidth - 10) + 'px';        
        if (!map.getBounds().contains(sw)) {
          this.div_.style.top = (anchorPixel.y - iwHeight - 100) + 'px';
        }        
      }
    
    
    };