Places UI Kit: A ready-to-use library that provides room for customization and low-code development. Try it out, and share yourinput on your UI Kit experience.

Handle mouse events

  • This guide explains how to make Data-driven Styling (DDS) boundaries on a map interactive by responding to mouse clicks and movements.

  • Users can register event listeners forclick andmousemove events on a DDS feature layer to trigger custom actions.

  • The example provided demonstrates styling changes and displaying an info window with boundary information upon interaction.

  • It's crucial to setfillOpacity to 0.1 or greater for the default style to enable user interactions with the boundaries.

Select platform:AndroidiOSJavaScript

Overview

Make a feature layer respond tomousemove andclick events, and usethem to return information about the boundary that was clicked. This informationincludes ID, display name, and feature type. The following example map shows theboundaries for Administrative Area Level 2, and has event handling code thatstyles polygons based on user interaction (mousemove changes the borderthickness,click shades the background color and causes an info window todisplay).

Enable feature layer events

Take the following steps to enable events on a feature layer:

  1. Register a feature layer for event notifications by calling theaddListener() function on the feature layer for each event you want toregister. In this example the map also gets a listener.

    TypeScript

    // Add the feature layer.featureLayer=innerMap.getFeatureLayer(google.maps.FeatureType.ADMINISTRATIVE_AREA_LEVEL_2);// Add the event listeners for the feature layer.featureLayer.addListener('click',handleClick);featureLayer.addListener('mousemove',handleMouseMove);// Map event listener.innerMap.addListener('mousemove',()=>{// If the map gets a mousemove, that means there are no feature layers// with listeners registered under the mouse, so we clear the last// interacted feature ids.if(lastInteractedFeatureIds?.length){lastInteractedFeatureIds=[];featureLayer.style=applyStyle;}});
    Note: Read theguide on using TypeScript and Google Maps.

    JavaScript

    // Add the feature layer.featureLayer=innerMap.getFeatureLayer(google.maps.FeatureType.ADMINISTRATIVE_AREA_LEVEL_2);// Add the event listeners for the feature layer.featureLayer.addListener('click',handleClick);featureLayer.addListener('mousemove',handleMouseMove);// Map event listener.innerMap.addListener('mousemove',()=>{// If the map gets a mousemove, that means there are no feature layers// with listeners registered under the mouse, so we clear the last// interacted feature ids.if(lastInteractedFeatureIds?.length){lastInteractedFeatureIds=[];featureLayer.style=applyStyle;}});

  2. Add event handler code to style the selected polygon based on the type ofinteraction.

    TypeScript

    functionhandleClick(/* MouseEvent */e){lastClickedFeatureIds=e.features.map((f)=>f.placeId);lastInteractedFeatureIds=[];featureLayer.style=applyStyle;createInfoWindow(e);}functionhandleMouseMove(/* MouseEvent */e){lastInteractedFeatureIds=e.features.map((f)=>f.placeId);featureLayer.style=applyStyle;}
    Note: Read theguide on using TypeScript and Google Maps.

    JavaScript

    functionhandleClick(/* MouseEvent */e){lastClickedFeatureIds=e.features.map((f)=>f.placeId);lastInteractedFeatureIds=[];featureLayer.style=applyStyle;createInfoWindow(e);}functionhandleMouseMove(/* MouseEvent */e){lastInteractedFeatureIds=e.features.map((f)=>f.placeId);featureLayer.style=applyStyle;}

  3. Use a feature style function to apply styles. The feature style functionshown here conditionally applies style based on the type of interaction. Threestyles are defined here: one to make the border bold onmousemove, one tochange the background and show an info window onclick, and a default style.

    TypeScript

    // Define styles.// Stroke and fill with minimum opacity value.conststyleDefault={strokeColor:'#810FCB',strokeOpacity:1.0,strokeWeight:2.0,fillColor:'white',fillOpacity:0.1,// Polygons must be visible to receive events.};// Style for the clicked polygon.conststyleClicked={...styleDefault,fillColor:'#810FCB',fillOpacity:0.5,};// Style for polygon on mouse move.conststyleMouseMove={...styleDefault,strokeWeight:4.0,};// Apply styles using a feature style function.functionapplyStyle(/* FeatureStyleFunctionOptions */params){constplaceId=params.feature.placeId;//@ts-ignoreif(lastClickedFeatureIds.includes(placeId)){returnstyleClicked;}//@ts-ignoreif(lastInteractedFeatureIds.includes(placeId)){returnstyleMouseMove;}returnstyleDefault;}
    Note: Read theguide on using TypeScript and Google Maps.

    JavaScript

    // Define styles.// Stroke and fill with minimum opacity value.conststyleDefault={strokeColor:'#810FCB',strokeOpacity:1.0,strokeWeight:2.0,fillColor:'white',fillOpacity:0.1,// Polygons must be visible to receive events.};// Style for the clicked polygon.conststyleClicked={...styleDefault,fillColor:'#810FCB',fillOpacity:0.5,};// Style for polygon on mouse move.conststyleMouseMove={...styleDefault,strokeWeight:4.0,};// Apply styles using a feature style function.functionapplyStyle(/* FeatureStyleFunctionOptions */params){constplaceId=params.feature.placeId;//@ts-ignoreif(lastClickedFeatureIds.includes(placeId)){returnstyleClicked;}//@ts-ignoreif(lastInteractedFeatureIds.includes(placeId)){returnstyleMouseMove;}returnstyleDefault;}

Note: Because only visible features can be interacted with, the default fillstyle should have afillOpacity of 0.1 or greater.

Complete example code

TypeScript

letinnerMap;letfeatureLayer;letinfoWindow;letlastInteractedFeatureIds=[];letlastClickedFeatureIds=[];functionhandleClick(/* MouseEvent */e){lastClickedFeatureIds=e.features.map((f)=>f.placeId);lastInteractedFeatureIds=[];featureLayer.style=applyStyle;createInfoWindow(e);}functionhandleMouseMove(/* MouseEvent */e){lastInteractedFeatureIds=e.features.map((f)=>f.placeId);featureLayer.style=applyStyle;}asyncfunctioninitMap(){// Request needed libraries.const{Map,InfoWindow}=(awaitgoogle.maps.importLibrary('maps'))asgoogle.maps.MapsLibrary;// Get the gmp-map element.constmapElement=document.querySelector('gmp-map')asgoogle.maps.MapElement;// Get the inner map.innerMap=mapElement.innerMap;// Set map options.innerMap.setOptions({mapTypeControl:false,});// Add the feature layer.featureLayer=innerMap.getFeatureLayer(google.maps.FeatureType.ADMINISTRATIVE_AREA_LEVEL_2);// Add the event listeners for the feature layer.featureLayer.addListener('click',handleClick);featureLayer.addListener('mousemove',handleMouseMove);// Map event listener.innerMap.addListener('mousemove',()=>{// If the map gets a mousemove, that means there are no feature layers// with listeners registered under the mouse, so we clear the last// interacted feature ids.if(lastInteractedFeatureIds?.length){lastInteractedFeatureIds=[];featureLayer.style=applyStyle;}});// Create the infowindow.infoWindow=newInfoWindow({});// Apply style on load, to enable clicking.featureLayer.style=applyStyle;}// Helper function for the infowindow.asyncfunctioncreateInfoWindow(event){letfeature=event.features[0];if(!feature.placeId)return;// Update the info window.// Get the place instance from the selected feature.constplace=awaitfeature.fetchPlace();// Create a new div to hold the text content.letcontent=document.createElement('div');// Get the text values.letnameText=document.createElement('span');nameText.textContent=`Display name:${place.displayName}`;letplaceIdText=document.createElement('span');placeIdText.textContent=`Place ID:${feature.placeId}`;letfeatureTypeText=document.createElement('span');featureTypeText.textContent=`Feature type:${feature.featureType}`;// Append the text to the div.content.appendChild(nameText);content.appendChild(document.createElement('br'));content.appendChild(placeIdText);content.appendChild(document.createElement('br'));content.appendChild(featureTypeText);updateInfoWindow(content,event.latLng);}// Define styles.// Stroke and fill with minimum opacity value.conststyleDefault={strokeColor:'#810FCB',strokeOpacity:1.0,strokeWeight:2.0,fillColor:'white',fillOpacity:0.1,// Polygons must be visible to receive events.};// Style for the clicked polygon.conststyleClicked={...styleDefault,fillColor:'#810FCB',fillOpacity:0.5,};// Style for polygon on mouse move.conststyleMouseMove={...styleDefault,strokeWeight:4.0,};// Apply styles using a feature style function.functionapplyStyle(/* FeatureStyleFunctionOptions */params){constplaceId=params.feature.placeId;//@ts-ignoreif(lastClickedFeatureIds.includes(placeId)){returnstyleClicked;}//@ts-ignoreif(lastInteractedFeatureIds.includes(placeId)){returnstyleMouseMove;}returnstyleDefault;}// Helper function to create an info window.functionupdateInfoWindow(content,center){infoWindow.setContent(content);infoWindow.setPosition(center);infoWindow.open({map:innerMap,shouldFocus:false,});}initMap();
Note: Read theguide on using TypeScript and Google Maps.

JavaScript

letinnerMap;letfeatureLayer;letinfoWindow;letlastInteractedFeatureIds=[];letlastClickedFeatureIds=[];functionhandleClick(/* MouseEvent */e){lastClickedFeatureIds=e.features.map((f)=>f.placeId);lastInteractedFeatureIds=[];featureLayer.style=applyStyle;createInfoWindow(e);}functionhandleMouseMove(/* MouseEvent */e){lastInteractedFeatureIds=e.features.map((f)=>f.placeId);featureLayer.style=applyStyle;}asyncfunctioninitMap(){// Request needed libraries.const{Map,InfoWindow}=(awaitgoogle.maps.importLibrary('maps'));// Get the gmp-map element.constmapElement=document.querySelector('gmp-map');// Get the inner map.innerMap=mapElement.innerMap;// Set map options.innerMap.setOptions({mapTypeControl:false,});// Add the feature layer.featureLayer=innerMap.getFeatureLayer(google.maps.FeatureType.ADMINISTRATIVE_AREA_LEVEL_2);// Add the event listeners for the feature layer.featureLayer.addListener('click',handleClick);featureLayer.addListener('mousemove',handleMouseMove);// Map event listener.innerMap.addListener('mousemove',()=>{// If the map gets a mousemove, that means there are no feature layers// with listeners registered under the mouse, so we clear the last// interacted feature ids.if(lastInteractedFeatureIds?.length){lastInteractedFeatureIds=[];featureLayer.style=applyStyle;}});// Create the infowindow.infoWindow=newInfoWindow({});// Apply style on load, to enable clicking.featureLayer.style=applyStyle;}// Helper function for the infowindow.asyncfunctioncreateInfoWindow(event){letfeature=event.features[0];if(!feature.placeId)return;// Update the info window.// Get the place instance from the selected feature.constplace=awaitfeature.fetchPlace();// Create a new div to hold the text content.letcontent=document.createElement('div');// Get the text values.letnameText=document.createElement('span');nameText.textContent=`Display name:${place.displayName}`;letplaceIdText=document.createElement('span');placeIdText.textContent=`Place ID:${feature.placeId}`;letfeatureTypeText=document.createElement('span');featureTypeText.textContent=`Feature type:${feature.featureType}`;// Append the text to the div.content.appendChild(nameText);content.appendChild(document.createElement('br'));content.appendChild(placeIdText);content.appendChild(document.createElement('br'));content.appendChild(featureTypeText);updateInfoWindow(content,event.latLng);}// Define styles.// Stroke and fill with minimum opacity value.conststyleDefault={strokeColor:'#810FCB',strokeOpacity:1.0,strokeWeight:2.0,fillColor:'white',fillOpacity:0.1,// Polygons must be visible to receive events.};// Style for the clicked polygon.conststyleClicked={...styleDefault,fillColor:'#810FCB',fillOpacity:0.5,};// Style for polygon on mouse move.conststyleMouseMove={...styleDefault,strokeWeight:4.0,};// Apply styles using a feature style function.functionapplyStyle(/* FeatureStyleFunctionOptions */params){constplaceId=params.feature.placeId;//@ts-ignoreif(lastClickedFeatureIds.includes(placeId)){returnstyleClicked;}//@ts-ignoreif(lastInteractedFeatureIds.includes(placeId)){returnstyleMouseMove;}returnstyleDefault;}// Helper function to create an info window.functionupdateInfoWindow(content,center){infoWindow.setContent(content);infoWindow.setPosition(center);infoWindow.open({map:innerMap,shouldFocus:false,});}initMap();
Note: The JavaScript is compiled from the TypeScript snippet.

CSS

/* * Always set the map height explicitly to define the size of the div element * that contains the map. */gmp-map{height:100%;}/* * Optional: Makes the sample page fill the window. */html,body{height:100%;margin:0;padding:0;}

HTML

<html>    <head>        <title>Handle Region Boundary Click Event</title>        <link rel="stylesheet" type="text/css" href="./style.css" />        <script type="module" src="./index.js"></script>        <!-- 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: "AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8", v: "weekly"});</script>    </head>    <body>        <gmp-map            center="39.23,-105.73"            zoom="8"            map-id="8b37d7206ccf0121a2634fd5"></gmp-map>    </body></html>

Try Sample

Learn more about events

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2025-11-21 UTC.