本頁說明可透過程式輔助方式監聽及處理的使用者介面事件和錯誤事件。
使用者介面事件
瀏覽器中的 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'
事件通常會傳遞 MouseEvent
,當中含有 latLng
屬性,代表地圖上獲得點擊的位置。請注意,這是使用者介面事件的專屬行為,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> <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script> <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 與不在 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 */ };