本页面介绍了可通过程序化方式监听和处理的界面事件及错误事件。
界面事件
浏览器中的 JavaScript 是由事件驱动的,这意味着 JavaScript 会通过生成事件来响应互动,并预期程序会监听其关注的事件。事件分为两种类型:
- 用户事件(例如“点击”鼠标事件)会从 DOM 传播到 Maps JavaScript API。这些事件与标准的 DOM 事件相互独立且截然不同。
- MVC 状态变化通知则反映了 Maps JavaScript API 对象中的变化,并按照
property_changed
规范命名。
每个 Maps JavaScript API 对象均可导出大量已命名的事件。关注某些事件的程序会为这些事件注册 JavaScript 事件监听器,并在收到这些事件时通过调用 addListener()
在该对象中注册事件处理脚本来执行相应代码。
以下示例将展示当您与地图互动时 google.maps.Map
会触发哪些事件。
如需查看完整的事件列表,请参阅 Maps JavaScript API 参考文档。对于包含事件的各个对象,事件会在单独的部分中列出。
界面事件
Maps JavaScript API 中的一些对象旨在响应用户事件,例如鼠标事件或键盘事件。例如,下面是 google.maps.marker.AdvancedMarkerElement
对象可以监听的一些用户事件:
'click'
'drag'
'dragend'
'dragstart'
'gmp-click'
如需查看完整列表,请参阅 AdvancedMarkerElement 类。这些事件可能看起来像标准 DOM 事件,但实际上却是 Maps JavaScript API 的一部分。由于不同的浏览器实现的 DOM 事件模型不同,因此 Maps JavaScript API 提供这些机制来监听和响应 DOM 事件,这样就无需处理各种跨浏览器特性。这些事件通常还会在表示某种界面状态(例如鼠标位置)的事件中传递参数。
MVC 状态更改
MVC 对象通常都包含状态。每当一个对象的属性发生变化时,Maps JavaScript API 就会触发一个事件来表示该属性已更改。例如,当地图的缩放级别发生变化后,API 就会在该地图上触发 zoom_changed
事件。您也可以通过调用 addListener()
在相应对象中注册事件处理脚本来截取这些状态变化。
用户事件与 MVC 状态变化可能看起来相似,但通常情况下您需要在代码中对它们进行不同的处理。例如,MVC 事件不在其事件中传递参数。您需要调用该对象中的相应 getProperty
方法来检查在 MVC 状态变化时发生更改的属性。
处理事件
若要注册接收事件通知,请使用 addListener()
事件处理脚本。该方法使用的参数是:一个要监听的事件,以及在指定的事件发生时要调用的一个函数。
示例:地图和标记事件
以下代码组合使用了用户事件和状态变化事件。我们为在被点击后会对地图执行缩放操作的标记附加了事件处理脚本。此外,还向地图添加了事件处理脚本来处理对 center
属性的更改,并在收到 center_changed
事件 3 秒后将地图平移回标记处:
TypeScript
async function initMap() { // Request needed libraries. const { Map } = await google.maps.importLibrary("maps") as google.maps.MapsLibrary; const { AdvancedMarkerElement } = await google.maps.importLibrary("marker") as google.maps.MarkerLibrary; const myLatlng = { lat: -25.363, lng: 131.044 }; const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 4, center: myLatlng, mapId: "DEMO_MAP_ID", } ); const marker = new google.maps.marker.AdvancedMarkerElement({ position: myLatlng, map, title: "Click to zoom", }); map.addListener("center_changed", () => { // 3 seconds after the center of the map has changed, pan back to the // marker. window.setTimeout(() => { map.panTo(marker.position as google.maps.LatLng); }, 3000); }); marker.addListener("click", () => { map.setZoom(8); map.setCenter(marker.position as google.maps.LatLng); }); } initMap();
JavaScript
async function initMap() { // Request needed libraries. const { Map } = await google.maps.importLibrary("maps"); const { AdvancedMarkerElement } = await google.maps.importLibrary("marker"); const myLatlng = { lat: -25.363, lng: 131.044 }; const map = new google.maps.Map(document.getElementById("map"), { zoom: 4, center: myLatlng, mapId: "DEMO_MAP_ID", }); const marker = new google.maps.marker.AdvancedMarkerElement({ position: myLatlng, map, title: "Click to zoom", }); map.addListener("center_changed", () => { // 3 seconds after the center of the map has changed, pan back to the // marker. window.setTimeout(() => { map.panTo(marker.position); }, 3000); }); marker.addListener("click", () => { map.setZoom(8); map.setCenter(marker.position); }); } initMap();
试用示例
提示:如果您要尝试检测视口中的变化,请务必使用特定的 bounds_changed
事件,而非其组成部分 zoom_changed
和 center_changed
事件。由于 Maps JavaScript API 会单独触发后面的两个事件,因此,只有在系统强制更改了视口后,getBounds()
才会报告实用结果。如果您想在此类事件之后执行 getBounds()
,请务必改为监听 bounds_changed
事件。
示例:形状修改和拖动事件
修改或拖动形状时,系统会在操作完成后触发相应的事件。如需查看事件列表和一些代码段,请参阅形状。
访问界面事件中的参数
Maps JavaScript API 中的界面事件通常会传递事件参数(可通过事件监听器访问),这些参数表示事件发生时的界面状态。例如,界面 'click'
事件通常会传递一个包含 latLng
属性的 MouseEvent
,该属性指示了地图上的点击位置。请注意,这是界面事件所独有的行为;MVC 状态更改不会在其事件中传递参数。
您可以采用访问对象属性的方式访问事件监听器中的事件参数。下例介绍了如何为地图添加事件监听器,以及如何在用户点击地图时在所点击的位置创建标记。
TypeScript
async function initMap() { // Request needed libraries. const { Map } = await google.maps.importLibrary("maps") as google.maps.MapsLibrary; const { AdvancedMarkerElement, PinElement } = await google.maps.importLibrary("marker") as google.maps.MarkerLibrary; const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 4, center: { lat: -25.363882, lng: 131.044922 }, mapId: "DEMO_MAP_ID", } ); map.addListener("click", (e) => { placeMarkerAndPanTo(e.latLng, map); }); } function placeMarkerAndPanTo(latLng: google.maps.LatLng, map: google.maps.Map) { new google.maps.marker.AdvancedMarkerElement({ position: latLng, map: map, }); map.panTo(latLng); } initMap();
JavaScript
async function initMap() { // Request needed libraries. const { Map } = await google.maps.importLibrary("maps"); const { AdvancedMarkerElement, PinElement } = await google.maps.importLibrary( "marker", ); const map = new google.maps.Map(document.getElementById("map"), { zoom: 4, center: { lat: -25.363882, lng: 131.044922 }, mapId: "DEMO_MAP_ID", }); map.addListener("click", (e) => { placeMarkerAndPanTo(e.latLng, map); }); } function placeMarkerAndPanTo(latLng, map) { new google.maps.marker.AdvancedMarkerElement({ position: latLng, map: map, }); map.panTo(latLng); } initMap();
试用示例
在事件监听器中使用闭包
在执行事件监听器时,将私有数据和持久性数据附加到对象通常是有好处的。JavaScript 不支持“私有”实例数据,但支持允许内部函数访问外部变量的闭包。在事件监听器中,闭包非常适用于访问通常未附加到发生事件的对象中的变量。
以下示例在事件监听器中使用了函数闭包将私密消息分配给一组标记。点击每个标记将会看到私密消息的一部分,但标记本身并不包含该消息。
TypeScript
async function initMap() { // Request needed libraries. const { Map } = await google.maps.importLibrary("maps") as google.maps.MapsLibrary; const { AdvancedMarkerElement } = await google.maps.importLibrary("marker") as google.maps.MarkerLibrary; const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 4, center: { lat: -25.363882, lng: 131.044922 }, mapId: "DEMO_MAP_ID", } ); const bounds: google.maps.LatLngBoundsLiteral = { north: -25.363882, south: -31.203405, east: 131.044922, west: 125.244141, }; // Display the area between the location southWest and northEast. map.fitBounds(bounds); // Add 5 markers to map at random locations. // For each of these markers, give them a title with their index, and when // they are clicked they should open an infowindow with text from a secret // message. const secretMessages = ["This", "is", "the", "secret", "message"]; const lngSpan = bounds.east - bounds.west; const latSpan = bounds.north - bounds.south; for (let i = 0; i < secretMessages.length; ++i) { const marker = new google.maps.marker.AdvancedMarkerElement({ position: { lat: bounds.south + latSpan * Math.random(), lng: bounds.west + lngSpan * Math.random(), }, map: map, }); attachSecretMessage(marker, secretMessages[i]); } } // Attaches an info window to a marker with the provided message. When the // marker is clicked, the info window will open with the secret message. function attachSecretMessage( marker: google.maps.marker.AdvancedMarkerElement, secretMessage: string ) { const infowindow = new google.maps.InfoWindow({ content: secretMessage, }); marker.addListener("click", () => { infowindow.open(marker.map, marker); }); } initMap();
JavaScript
async function initMap() { // Request needed libraries. const { Map } = await google.maps.importLibrary("maps"); const { AdvancedMarkerElement } = await google.maps.importLibrary("marker"); const map = new google.maps.Map(document.getElementById("map"), { zoom: 4, center: { lat: -25.363882, lng: 131.044922 }, mapId: "DEMO_MAP_ID", }); const bounds = { north: -25.363882, south: -31.203405, east: 131.044922, west: 125.244141, }; // Display the area between the location southWest and northEast. map.fitBounds(bounds); // Add 5 markers to map at random locations. // For each of these markers, give them a title with their index, and when // they are clicked they should open an infowindow with text from a secret // message. const secretMessages = ["This", "is", "the", "secret", "message"]; const lngSpan = bounds.east - bounds.west; const latSpan = bounds.north - bounds.south; for (let i = 0; i < secretMessages.length; ++i) { const marker = new google.maps.marker.AdvancedMarkerElement({ position: { lat: bounds.south + latSpan * Math.random(), lng: bounds.west + lngSpan * Math.random(), }, map: map, }); attachSecretMessage(marker, secretMessages[i]); } } // Attaches an info window to a marker with the provided message. When the // marker is clicked, the info window will open with the secret message. function attachSecretMessage(marker, secretMessage) { const infowindow = new google.maps.InfoWindow({ content: secretMessage, }); marker.addListener("click", () => { infowindow.open(marker.map, marker); }); } initMap();
试用示例
获取和设置事件处理脚本中的属性
事件触发时,Maps JavaScript API 事件系统中的所有 MVC 状态更改事件都不会传递参数(用户事件确实会传递参数,这是可以检查到的)。如果您需要检查有关 MVC 状态更改的属性,应针对该对象显式调用相应的 getProperty()
方法。在此检查过程中,系统会始终检索 MVC 对象的当前状态,但该状态可能不是事件首次触发时的状态。
注意:如果在事件处理脚本中显式设置一个属性,而该事件处理脚本会响应这个特定属性的状态更改,则可能会产生不可预期和/或不必要的行为。例如,设置此类属性会触发新的事件,如果您总是在此事件处理脚本中设置属性,最终可能会出现无限循环的情况。
在以下示例中,我们会设置一个事件处理脚本来响应缩放事件,具体方法是弹出显示相应缩放级别的信息窗口。
TypeScript
async function initMap() { // Request needed libraries. const { Map } = await google.maps.importLibrary("maps") as google.maps.MapsLibrary; const originalMapCenter = new google.maps.LatLng(-25.363882, 131.044922); const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 4, center: originalMapCenter, } ); const infowindow = new google.maps.InfoWindow({ content: "Change the zoom level", position: originalMapCenter, }); infowindow.open(map); map.addListener("zoom_changed", () => { infowindow.setContent("Zoom: " + map.getZoom()!); }); } initMap();
JavaScript
async function initMap() { // Request needed libraries. const { Map } = await google.maps.importLibrary("maps"); const originalMapCenter = new google.maps.LatLng(-25.363882, 131.044922); const map = new google.maps.Map(document.getElementById("map"), { zoom: 4, center: originalMapCenter, }); const infowindow = new google.maps.InfoWindow({ content: "Change the zoom level", position: originalMapCenter, }); infowindow.open(map); map.addListener("zoom_changed", () => { infowindow.setContent("Zoom: " + map.getZoom()); }); } initMap();
试用示例
监听 DOM 事件
Maps JavaScript API 事件模型会自行创建并管理自定义事件。不过,浏览器内的 DOM(文档对象模型)也会根据所使用的特定浏览器事件模型自行创建并分派事件。如果您想要捕获并响应这些事件,可以使用 Maps JavaScript API 提供的 addDomListener()
静态方法来监听并绑定到这些 DOM 事件。
该方法易于使用,具有如下所示的签名:
addDomListener(instance:Object, eventName:string, handler:Function)
其中 instance
可能是浏览器支持的任意 DOM 元素,包括:
- DOM 的分层组成部分,例如
window
或document.body.myform
- 已命名的元素,例如
document.getElementById("foo")
请注意,addDomListener()
会将指明的事件传递给浏览器,以便系统根据浏览器的 DOM 事件模型处理该事件;不过,几乎所有的现代浏览器都至少支持 DOM 级别 2(如需详细了解 DOM 级别事件,请参阅 Mozilla DOM 级别参考文档)。
TypeScript
async function initMap() { // Request needed libraries. const { Map } = await google.maps.importLibrary("maps") as google.maps.MapsLibrary; const mapDiv = document.getElementById("map") as HTMLElement; const map = new google.maps.Map(mapDiv, { zoom: 8, center: new google.maps.LatLng(-34.397, 150.644), }); // We add a DOM event here to show an alert if the DIV containing the // map is clicked. google.maps.event.addDomListener(mapDiv, "click", () => { window.alert("Map was clicked!"); }); } initMap();
JavaScript
async function initMap() { // Request needed libraries. const { Map } = await google.maps.importLibrary("maps"); const mapDiv = document.getElementById("map"); const map = new google.maps.Map(mapDiv, { zoom: 8, center: new google.maps.LatLng(-34.397, 150.644), }); // We add a DOM event here to show an alert if the DIV containing the // map is clicked. google.maps.event.addDomListener(mapDiv, "click", () => { window.alert("Map was clicked!"); }); } initMap();
HTML
<html> <head> <title>Listening to DOM Events</title> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="map"></div> <!-- 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: "AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg", v: "weekly"});</script> </body> </html>
试用示例
尽管上述代码是 Maps JavaScript API 代码,但 addDomListener()
方法会绑定到浏览器的 window
对象,并允许该 API 与其常规网域之外的对象进行通信。
移除事件监听器
若要移除特定事件监听器,必须已经将其分配给一个变量。然后,您可以调用 removeListener()
,并传入已分配监听器的变量名称。
var listener1 = marker.addListener('click', aFunction); google.maps.event.removeListener(listener1);
若要从特定实例中移除所有监听器,请调用 clearInstanceListeners()
并向其传递实例名称。
var listener1 = marker.addListener('click', aFunction); var listener2 = marker.addListener('mouseover', bFunction); // Remove listener1 and listener2 from marker instance. google.maps.event.clearInstanceListeners(marker);
若要从特定实例中移除针对特定事件类型的所有监听器,请调用 clearListeners()
,并传入实例名称和事件名称。
marker.addListener('click', aFunction); marker.addListener('click', bFunction); marker.addListener('click', cFunction); // Remove all click listeners from marker instance. google.maps.event.clearListeners(marker, 'click');
如需了解详情,请参阅 google.maps.event 命名空间的参考文档。
监听身份验证错误
如果您想通过程序化方式检测身份验证失败的情况(例如自动发送信标),可以准备一个回调函数。如果定义了以下全局函数,系统将在身份验证失败时调用它。
function gm_authFailure() { /* Code */ };