Here you go:
http://jsfiddle.net/nickaknudson/KVa2d/
tooltip = new Tooltip("text"); ... tooltip.open(map, marker);
Customizable with CSS.
UPDATE
Commented code: http://jsfiddle.net/nickaknudson/KVa2d/12/
UPDATE 2
Unnecessary bits removed: http://jsfiddle.net/nickaknudson/KVa2d/14/
//======================== // Tooltip Class Definition // extends OverlayView: // https://developers.google.com/maps/documentation/javascript/reference#OverlayView //======================== var Tooltip Tooltip = function(tip) { this.tip = tip; this.buildDOM(); }; $.extend(Tooltip.prototype, google.maps.OverlayView.prototype, { // build the DOM buildDOM: function() { // Body DIV this.bdiv = $("<div></div>").addClass('WindowBody').html(this.tip); // Window DIV this.wdiv = $("<div></div>").addClass('Window').append(this.bdiv); // Shadow DIV this.sdiv = $("<div></div>").addClass('WindowShadow'); // Start Closed this.close(); }, // API - onAdd onAdd: function() { $(this.getPanes().floatPane).append(this.wdiv); $(this.getPanes().floatShadow).append(this.sdiv); }, // API - onRemove onRemove: function() { this.wdiv.detach(); this.sdiv.detach(); }, // API - draw draw: function() { var pos, left, top; // projection is accessible? if (!this.getProjection()) return; // position is accessible? if (!this.get('position')) return; // convert projection pos = this.getProjection().fromLatLngToDivPixel(this.get('position')); // top offset top = pos.y - this.getAnchorHeight() / 2; // left offset if (this.getMap().getCenter().lng() > this.get('position').lng()) { left = pos.x + this.wdiv.width() * 0.5; } else { left = pos.x - this.wdiv.width() * 1.5; } // window position this.wdiv.css('top', top); this.wdiv.css('left', left); // shadow position this.sdiv.css('top', (top - this.getAnchorHeight() / 2)); this.sdiv.css('left', left); // shadow size this.sdiv.width(this.wdiv.width()); this.sdiv.height(this.wdiv.height()); }, // open Tooltip open: function(map, anchor) { // bind to map if (map) this.setMap(map); // bind to anchor if (anchor) { this.set('anchor', anchor); this.bindTo('anchorPoint', anchor); this.bindTo('position', anchor); } // need to force redraw otherwise it will decide to draw after we show the Tooltip this.draw(); // show tooltip this.wdiv.show(); this.sdiv.show(); // set property this.isOpen = true; }, // close Tooltip close: function() { // hide tooltip this.wdiv.hide(); this.sdiv.hide(); // set property this.isOpen = false; }, // correctly get the anchorPoint height getAnchorHeight: function() { // See: https://developers.google.com/maps/documentation/javascript/reference#InfoWindow // "The anchorPoint is the offset from the anchor position to the tip of the InfoWindow." return -1 * this.get('anchorPoint').y; } });
UPDATE 3
Better positioning using the outerWidth () and outerHeight () methods to account for borders, etc. Removed shadow div.
http://jsfiddle.net/nickaknudson/KVa2d/16/
//======================== // Tooltip Class Definition // extends OverlayView: // https://developers.google.com/maps/documentation/javascript/reference#OverlayView //======================== var Tooltip Tooltip = function(tip) { this.tip = tip; this.buildDOM(); }; $.extend(Tooltip.prototype, google.maps.OverlayView.prototype, { // build the DOM buildDOM: function() { // Window DIV this.wdiv = $("<div></div>").addClass('Window').append(this.tip); // Start Closed this.close(); }, // API - onAdd onAdd: function() { $(this.getPanes().floatPane).append(this.wdiv); }, // API - onRemove onRemove: function() { this.wdiv.detach(); }, // API - draw draw: function() { var pos, left, top; // projection is accessible? if (!this.getProjection()) return; // position is accessible? if (!this.get('position')) return; // convert projection pos = this.getProjection().fromLatLngToDivPixel(this.get('position')); // top offset top = pos.y - this.getAnchorHeight() / 2 - this.wdiv.outerHeight()/2; // left offset if (this.getMap().getCenter().lng() > this.get('position').lng()) { left = pos.x + this.wdiv.outerWidth() * 0.3; } else { left = pos.x - this.wdiv.outerWidth() * 1.3; } // window position this.wdiv.css('top', top); this.wdiv.css('left', left); }, // open Tooltip open: function(map, anchor) { // bind to map if (map) this.setMap(map); // bind to anchor if (anchor) { this.set('anchor', anchor); this.bindTo('anchorPoint', anchor); this.bindTo('position', anchor); } // need to force redraw otherwise it will decide to draw after we show the Tooltip this.draw(); // show tooltip this.wdiv.show(); // set property this.isOpen = true; }, // close Tooltip close: function() { // hide tooltip this.wdiv.hide(); // set property this.isOpen = false; }, // correctly get the anchorPoint height getAnchorHeight: function() { // See: https://developers.google.com/maps/documentation/javascript/reference#InfoWindow // "The anchorPoint is the offset from the anchor position to the tip of the InfoWindow." return -1 * this.get('anchorPoint').y; } });
Resources