javascriptgoogle-mapsnext.jsgoogle-maps-api-3google-maps-advanced-marker-element

NextJS - Google maps dynamically change the color of markers


In my nextJS app, I load my map via the @googlemaps/js-api-loader package. I have a list of locations I want to plot on the map, and at the same time have a list of them disabled on the left side of my page.

On hovering over one of the locations in the list, I would like to dynamically change the color of the marker that represents that location (kind of like how the current focus on the map as well). How can I do that?

here's my code so far:

type MarkerObject = {
    id: number
    marker: google.maps.marker.AdvancedMarkerElement
}

export const MapComponent = ({ locations, currentFocusId }: MapProps) => {
    const [markers, setMarkers] = useState<MarkerObject[]>([])

    useEffect(() => {
        const initMap = async () => {
            const loader = new Loader({ apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY! })
            const { Map } = (await loader.importLibrary('maps')) as google.maps.MapsLibrary
            // prettier-ignore
            const { AdvancedMarkerElement, PinElement } = (await loader.importLibrary('marker')) as google.maps.MarkerLibrary

            // create map
            const map = new Map(document.getElementById('map') as HTMLElement, {
                center: locations[0].position,
                zoom: 15,
                mapId: 'map',
            })

            // create markers
            for (const location of locations) {
                const pinBackground = new PinElement({
                    borderColor: 'black',
                    glyphColor: 'black',
                })

                const marker = new AdvancedMarkerElement({
                    map,
                    position: location.position,
                    content: pinBackground.element,
                })
                setMarkers((prev) => [...prev, { id: location.id, marker }])
            }
        }

        initMap()
    }, [])

useEffect(() => {
        if (!markers.length) return

        const focusMarker = markers.find((markerObject: MarkerObject) => markerObject.id === currentFocusId)
        if (!focusMarker) return

        // how can I change / replace the content or color of the marker to set it to blue?
    }, [currentFocusId])

    return <div id="map" style={{ width: '100vw', height: '100vh' }}></div>

I have tried stuff like

focusMarker.marker.content = new google.maps.marker.PinElement({
            background: 'blue',
            borderColor: 'black',
            glyphColor: 'black',
        })

but nothing so far has worked


Solution

  • Here is a simple example. Clicking the button will change the pin background color.

    1. Create a PinElement with the desired values
    2. Change the PinElement properties with dot notation
    3. Apply the modified PinElement content to the marker content property

    async function initMap() {
    
      // Request needed libraries.
      const { Map } = await google.maps.importLibrary("maps");
      const { AdvancedMarkerElement, PinElement } = await google.maps.importLibrary(
        "marker",
      );
    
      const center = {
        lat: 0,
        lng: 0
      }
    
      const map = new Map(document.getElementById("map"), {
        center: center,
        zoom: 5,
        mapId: "4504f8b37365c3d0",
      });
    
      const pinBackground = new PinElement({
        background: 'red'
      })
    
      const marker = new AdvancedMarkerElement({
        map,
        position: center,
        content: pinBackground.element,
        title: "A marker using PinElement",
      });
      
      document.getElementById('changePin').addEventListener('click', function() {
        
        // Change background to "yellow"
        pinBackground.background = 'yellow'
        marker.content = pinBackground.element
      });
    }
    
    initMap();
    #map {
      height: 140px;
      margin-bottom: 1em;
    }
    <div id="map"></div>
    <button id="changePin">Change pin color</button>
    
    <!-- prettier-ignore -->
    <script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})
            ({key: "AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk", v: "beta"});</script>