- {children}
+ // anchoring container
+
+ {/* AdvancedMarker div that user can give styles and classes */}
+
+ {children}
+
);
};
+export type CustomMarkerContent =
+ | (HTMLDivElement & {isCustomMarker?: boolean})
+ | null;
+
export type AdvancedMarkerRef = google.maps.marker.AdvancedMarkerElement | null;
function useAdvancedMarker(props: AdvancedMarkerProps) {
const [marker, setMarker] =
@@ -185,11 +186,14 @@ function useAdvancedMarker(props: AdvancedMarkerProps) {
setMarker(newMarker);
// create the container for marker content if there are children
- let contentElement: HTMLDivElement | null = null;
+ let contentElement: CustomMarkerContent = null;
if (numChildren > 0) {
contentElement = document.createElement('div');
- contentElement.style.width = '0';
- contentElement.style.height = '0';
+
+ // We need some kind of flag to identify the custom marker content
+ // in the infowindow component. Choosing a custom property instead of a className
+ // to not encourage users to style the marker content directly.
+ contentElement.isCustomMarker = true;
newMarker.content = contentElement;
setContentContainer(contentElement);
@@ -233,15 +237,31 @@ function useAdvancedMarker(props: AdvancedMarkerProps) {
else marker.gmpDraggable = false;
}, [marker, draggable, onDrag, onDragEnd, onDragStart]);
- // set gmpClickable from props (when unspecified, it's true if the onClick event
- // callback is specified)
+ // set gmpClickable from props (when unspecified, it's true if the onClick or one of
+ // the hover events callbacks are specified)
useEffect(() => {
if (!marker) return;
- if (clickable !== undefined) marker.gmpClickable = clickable;
- else if (onClick) marker.gmpClickable = true;
- else marker.gmpClickable = false;
- }, [marker, clickable, onClick]);
+ const gmpClickable =
+ clickable !== undefined ||
+ Boolean(onClick) ||
+ Boolean(onMouseEnter) ||
+ Boolean(onMouseLeave);
+
+ // gmpClickable is only available in beta version of the
+ // maps api (as of 2024-10-10)
+ marker.gmpClickable = gmpClickable;
+
+ // enable pointer events for the markers with custom content
+ if (gmpClickable && marker?.content && isElementNode(marker.content)) {
+ marker.content.style.pointerEvents = 'none';
+
+ if (marker.content.firstElementChild) {
+ (marker.content.firstElementChild as HTMLElement).style.pointerEvents =
+ 'all';
+ }
+ }
+ }, [marker, clickable, onClick, onMouseEnter, onMouseLeave]);
useMapsEventListener(marker, 'click', onClick);
useMapsEventListener(marker, 'drag', onDrag);
diff --git a/src/components/info-window.tsx b/src/components/info-window.tsx
index 8cdfaad2..b4ed26c1 100644
--- a/src/components/info-window.tsx
+++ b/src/components/info-window.tsx
@@ -14,7 +14,7 @@ import {useMapsEventListener} from '../hooks/use-maps-event-listener';
import {setValueForStyles} from '../libraries/set-value-for-styles';
import {useMapsLibrary} from '../hooks/use-maps-library';
import {useDeepCompareEffect} from '../libraries/use-deep-compare-effect';
-import {isAdvancedMarker} from './advanced-marker';
+import {CustomMarkerContent, isAdvancedMarker} from './advanced-marker';
export type InfoWindowProps = Omit<
google.maps.InfoWindowOptions,
@@ -180,23 +180,26 @@ export const InfoWindow = (props: PropsWithChildren
) => {
// Only do the infowindow adjusting when dealing with an AdvancedMarker
if (isAdvancedMarker(anchor) && anchor.content instanceof Element) {
- const wrapperBcr = anchor.content.getBoundingClientRect() ?? {};
- const {width: anchorWidth, height: anchorHeight} = wrapperBcr;
+ const wrapper = anchor.content as CustomMarkerContent;
+ const wrapperBcr = wrapper?.getBoundingClientRect();
// This checks whether or not the anchor has custom content with our own
// div wrapper. If not, that means we have a regular AdvancedMarker without any children.
// In that case we do not want to adjust the infowindow since it is all handled correctly
// by the Google Maps API.
- if (anchorWidth === 0 && anchorHeight === 0) {
+ if (wrapperBcr && wrapper?.isCustomMarker) {
// We can safely typecast here since we control that element and we know that
// it is a div
- const anchorDomContent = anchor.content.firstElementChild as Element;
+ const anchorDomContent = anchor.content.firstElementChild
+ ?.firstElementChild as Element;
const contentBcr = anchorDomContent?.getBoundingClientRect();
// center infowindow above marker
const anchorOffsetX =
- contentBcr.x - wrapperBcr.x + contentBcr.width / 2;
+ contentBcr.x -
+ wrapperBcr.x +
+ (contentBcr.width - wrapperBcr.width) / 2;
const anchorOffsetY = contentBcr.y - wrapperBcr.y;
const opts: google.maps.InfoWindowOptions = infoWindowOptions;
diff --git a/src/components/pin.tsx b/src/components/pin.tsx
index f5549c1f..c1e725e4 100644
--- a/src/components/pin.tsx
+++ b/src/components/pin.tsx
@@ -58,7 +58,10 @@ export const Pin = (props: PropsWithChildren) => {
}
// Set content of Advanced Marker View to the Pin View element
- const markerContent = advancedMarker.content?.firstChild;
+ // Here we are selecting the anchor container.
+ // The hierarchy is as follows:
+ // "advancedMarker.content" (from google) -> "pointer events reset div" -> "anchor container"
+ const markerContent = advancedMarker.content?.firstChild?.firstChild;
while (markerContent?.firstChild) {
markerContent.removeChild(markerContent.firstChild);