장소 검색이라고도 하는 PlaceListElement는 장소 검색 결과를 목록에 렌더링하는 HTML 요소입니다. gmp-place-list
요소를 구성하는 방법에는 두 가지가 있습니다.
- Search nearby request: Places Nearby Search의 검색 결과를 렌더링하도록 위젯을 구성합니다.
- 텍스트 요청으로 검색: 장소 텍스트 검색의 검색 결과를 렌더링하도록 위젯을 구성합니다.
주변 검색 요청
다음 예는 주변 검색에 대한 응답으로 장소 목록 요소를 렌더링합니다. 편의상 카페, 레스토랑, 전기자동차 충전소 등 세 가지 장소 유형만 표시됩니다. 결과를 선택하면 선택한 장소의 장소 세부정보를 보여주는 정보 창이 표시됩니다. 지도에 장소 목록 요소를 추가하려면 다음 스니펫과 같이 HTML 페이지에 gmp-place-list
요소를 추가합니다.
<gmp-map center="-37.813,144.963" zoom="10" map-id="DEMO_MAP_ID"> <div class="overlay" slot="control-inline-start-block-start"> <div class="controls"> <select name="types" class="type-select"> <option value="">Select a place type</option> <option value="cafe">Cafe</option> <option value="restaurant">Restaurant</option> <option value="electric_vehicle_charging_station"> EV charging station </option> </select> </div> <div class="list-container"> <gmp-place-list selectable style="display: none;"></gmp-place-list> </div> </div> </gmp-map> <gmp-place-details style="display: none;"> <gmp-place-details-place-request></gmp-place-details-place-request> <gmp-place-all-content></gmp-place-all-content> </gmp-place-details>
상호작용할 페이지 요소를 선택하는 데 여러 개의 querySelector
호출이 사용됩니다.
const map = document.querySelector("gmp-map"); const placeList = document.querySelector("gmp-place-list"); const typeSelect = document.querySelector(".type-select"); const placeDetails = document.querySelector("gmp-place-details"); const placeDetailsRequest = document.querySelector('gmp-place-details-place-request');
사용자가 검색 버튼을 클릭하면
configureFromSearchNearbyRequest
가 호출되고 장소 목록 요소에 결과가 표시됩니다 (마커는 addMarkers
도우미 함수에 추가됨). 다음 스니펫은 gmp-placeselect
이벤트를 사용하여 장소 목록에서 클릭 이벤트를 처리하는 코드도 보여줍니다.
placeDetails.addEventListener('gmp-load', (event) => { // Center the info window on the map. map.innerMap.fitBounds(placeDetails.place.viewport, { top: 500, left: 400 }); }); typeSelect.addEventListener('change', (event) => { // First remove all existing markers. for (marker in markers) { markers[marker].map = null; } markers = {}; if (typeSelect.value) { placeList.style.display = 'block'; placeList.configureFromSearchNearbyRequest({ locationRestriction: getContainingCircle(map.innerMap.getBounds()), includedPrimaryTypes: [typeSelect.value], }).then(addMarkers); // Handle user selection in Place Details. placeList.addEventListener('gmp-placeselect', ({ place }) => { markers[place.id].click(); }); } });
전체 코드 예 보기
자바스크립트
const map = document.querySelector("gmp-map"); const placeList = document.querySelector("gmp-place-list"); const typeSelect = document.querySelector(".type-select"); const placeDetails = document.querySelector("gmp-place-details"); const placeDetailsRequest = document.querySelector('gmp-place-details-place-request'); let markers = {}; let infoWindow; async function initMap() { await google.maps.importLibrary('places'); const { LatLngBounds } = await google.maps.importLibrary('core'); const { InfoWindow } = await google.maps.importLibrary('maps'); const { spherical } = await google.maps.importLibrary('geometry'); infoWindow = new InfoWindow; let marker; function getContainingCircle(bounds) { const diameter = spherical.computeDistanceBetween(bounds.getNorthEast(), bounds.getSouthWest()); const calculatedRadius = diameter / 2; const cappedRadius = Math.min(calculatedRadius, 50000); // Radius cannot be more than 50000. return { center: bounds.getCenter(), radius: cappedRadius }; } findCurrentLocation(); map.innerMap.setOptions({ mapTypeControl: false, clickableIcons: false, }); placeDetails.addEventListener('gmp-load', (event) => { // Center the info window on the map. map.innerMap.fitBounds(placeDetails.place.viewport, { top: 500, left: 400 }); }); typeSelect.addEventListener('change', (event) => { // First remove all existing markers. for (marker in markers) { markers[marker].map = null; } markers = {}; if (typeSelect.value) { placeList.style.display = 'block'; placeList.configureFromSearchNearbyRequest({ locationRestriction: getContainingCircle(map.innerMap.getBounds()), includedPrimaryTypes: [typeSelect.value], }).then(addMarkers); // Handle user selection in Place Details. placeList.addEventListener('gmp-placeselect', ({ place }) => { markers[place.id].click(); }); } }); } async function addMarkers() { const { AdvancedMarkerElement } = await google.maps.importLibrary('marker'); const { LatLngBounds } = await google.maps.importLibrary('core'); const bounds = new LatLngBounds(); if (placeList.places.length > 0) { placeList.places.forEach((place) => { let marker = new AdvancedMarkerElement({ map: map.innerMap, position: place.location }); markers[place.id] = marker; bounds.extend(place.location); marker.addListener('gmp-click', (event) => { if (infoWindow.isOpen) { infoWindow.close(); } placeDetailsRequest.place = place.id; placeDetails.style.display = 'block'; placeDetails.style.width = '350px'; infoWindow.setOptions({ content: placeDetails, }); infoWindow.open({ anchor: marker, map: map.innerMap }); }); map.innerMap.setCenter(bounds.getCenter()); map.innerMap.fitBounds(bounds); }); } } async function findCurrentLocation() { const { LatLng } = await google.maps.importLibrary('core'); if (navigator.geolocation) { navigator.geolocation.getCurrentPosition((position) => { const pos = new LatLng(position.coords.latitude, position.coords.longitude); map.innerMap.panTo(pos); map.innerMap.setZoom(14); }, () => { console.log('The Geolocation service failed.'); map.innerMap.setZoom(14); }); } else { console.log('Your browser doesn\'t support geolocation'); map.innerMap.setZoom(14); } } initMap();
CSS
html, body { height: 100%; margin: 0; } body { display: flex; flex-direction: column; font-family: Arial, Helvetica, sans-serif; } h1 { font-size: large; text-align: center; } gmp-map { box-sizing: border-box; height: 600px; } .overlay { position: relative; top: 40px; margin: 20px; width: 400px; } .controls { display: flex; gap: 10px; margin-bottom: 10px; height: 32px; } .search-button { background-color: #5491f5; color: #fff; border: 1px solid #ccc; border-radius: 5px; width: 100px; cursor: pointer; } .type-select { border: 1px solid #ccc; border-radius: 5px; flex-grow: 1; padding: 0 10px; } .list-container { height: 400px; overflow: auto; border-radius: 10px; } gmp-place-list { background-color: #fff; font-size: large }
HTML
<!DOCTYPE html> <html> <head> <title>Place List Nearby Search with Google Maps</title> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="style.css"> <script type="module" src="./index.js"></script> </head> <body> <gmp-map center="-37.813,144.963" zoom="10" map-id="DEMO_MAP_ID"> <div class="overlay" slot="control-inline-start-block-start"> <div class="controls"> <select name="types" class="type-select"> <option value="">Select a place type</option> <option value="cafe">Cafe</option> <option value="restaurant">Restaurant</option> <option value="electric_vehicle_charging_station"> EV charging station </option> </select> </div> <div class="list-container"> <gmp-place-list selectable style="display: none;"></gmp-place-list> </div> </div> </gmp-map> <gmp-place-details style="display: none;"> <gmp-place-details-place-request></gmp-place-details-place-request> <gmp-place-all-content></gmp-place-all-content> </gmp-place-details> <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: "alpha"});</script> </body> </html>
샘플 사용해 보기
텍스트로 검색 요청
이 예에서는 사용자 텍스트 검색에 대한 응답으로 장소 목록 요소를 렌더링합니다. 결과를 선택하면 선택한 장소의 장소 세부정보를 보여주는 정보 창이 표시됩니다. 지도에 장소 목록 요소를 추가하려면 다음 스니펫과 같이 HTML 페이지에 gmp-place-list
요소를 추가합니다.
<gmp-map center="37.395641,-122.077627" zoom="10" map-id="DEMO_MAP_ID"> <div class="overlay" slot="control-inline-start-block-start"> <div class="text-search-container"> <input type="text" id="textSearchInput" placeholder="Enter text search..."> <button id="textSearchButton">Search</button> </div> <div class="list-container"> <gmp-place-list selectable style="display: none;"></gmp-place-list> </div> </div> <gmp-place-details style="display: none;"> <gmp-place-details-place-request></gmp-place-details-place-request> <gmp-place-all-content></gmp-place-all-content> </gmp-place-details> </gmp-map>
상호작용할 페이지 요소를 선택하는 데 여러 개의 querySelector
호출이 사용됩니다.
const map = document.querySelector("gmp-map"); const placeList = document.querySelector("gmp-place-list"); const placeDetails = document.querySelector("gmp-place-details"); let marker = document.querySelector('gmp-advanced-marker'); const textSearchInput = document.getElementById('textSearchInput'); const textSearchButton = document.getElementById('textSearchButton'); const placeDetailsRequest = document.querySelector('gmp-place-details-place-request');
사용자가 검색어를 입력한 후 검색 함수가 실행되면
configureFromSearchByTextRequest
가 호출되고 장소 목록 요소가 결과를 렌더링합니다 (마커는 addMarkers
도우미 함수에 추가됨). 다음 스니펫은 함수의 코드를 보여줍니다.
async function searchByTextRequest() { if (textSearchInput.value !== "") { placeList.style.display = "block"; placeList.configureFromSearchByTextRequest({ locationRestriction: bounds, textQuery: textSearchInput.value, }).then(addMarkers); // Handle user selection in Place Details. placeList.addEventListener("gmp-placeselect", ({ place }) => { markers[place.id].click(); }); } }
전체 코드 예 보기
다음 예에서는 장소 목록 UI 구성요소를 사용하여
configureFromSearchNearbyRequest
를 통한 텍스트 검색을 기반으로 장소를 표시하고 클릭 가능한 마커를 지도에 추가하여 선택 시 장소 세부정보를 표시합니다.
자바스크립트
const map = document.querySelector("gmp-map"); const placeList = document.querySelector("gmp-place-list"); const placeDetails = document.querySelector("gmp-place-details"); let marker = document.querySelector('gmp-advanced-marker'); const textSearchInput = document.getElementById('textSearchInput'); const textSearchButton = document.getElementById('textSearchButton'); const placeDetailsRequest = document.querySelector('gmp-place-details-place-request'); let markers = {}; let infoWindow; let center = { lat: 37.395641, lng: -122.077627 }; // Mountain View, CA. let bounds; async function initMap() { const { Map, InfoWindow } = await google.maps.importLibrary("maps"); const { Place } = await google.maps.importLibrary("places"); // Set bounds for location restriction. bounds = new google.maps.LatLngBounds({ lat: 37.37808200917261, lng: -122.13741583377849 }, { lat: 37.416676154341324, lng: -122.02261728794109 }); infoWindow = new google.maps.InfoWindow; // Center the map map.innerMap.panTo(center); map.innerMap.setZoom(14); map.innerMap.setOptions({ mapTypeControl: false, clickableIcons: false, }); // Fire when the Place Details Element is loaded. placeDetails.addEventListener('gmp-load', (event) => { // Center the info window on the map. map.innerMap.fitBounds(placeDetails.place.viewport, { top: 500, left: 400 }); }); // Handle clicks on the search button. textSearchButton.addEventListener('click', searchByTextRequest); // Handle enter key on text input. textSearchInput.addEventListener('keydown', (event) => { if (event.key === 'Enter') { searchByTextRequest(); } }); } async function searchByTextRequest() { if (textSearchInput.value !== "") { placeList.style.display = "block"; placeList.configureFromSearchByTextRequest({ locationRestriction: bounds, textQuery: textSearchInput.value, }).then(addMarkers); // Handle user selection in Place Details. placeList.addEventListener("gmp-placeselect", ({ place }) => { markers[place.id].click(); }); } } async function addMarkers() { const { AdvancedMarkerElement } = await google.maps.importLibrary("marker"); const { LatLngBounds } = await google.maps.importLibrary("core"); const bounds = new LatLngBounds(); // First remove all existing markers. for (marker in markers) { markers[marker].map = null; } markers = {}; if (placeList.places.length > 0) { placeList.places.forEach((place) => { let marker = new AdvancedMarkerElement({ map: map.innerMap, position: place.location, }); markers[place.id] = marker; bounds.extend(place.location); marker.collisionBehavior = google.maps.CollisionBehavior.REQUIRED_AND_HIDES_OPTIONAL; marker.addListener('gmp-click', (event) => { if (infoWindow.isOpen) { infoWindow.close(); } // Set the Place Details request to the selected place. placeDetailsRequest.place = place.id; placeDetails.style.display = "block"; placeDetails.style.width = "350px"; infoWindow.setOptions({ content: placeDetails }); infoWindow.open({ anchor: marker, map: map.innerMap }); placeDetails.addEventListener('gmp-load', () => { map.innerMap.fitBounds(place.viewport, { top: 400, left: 400 }); }); }); map.innerMap.setCenter(bounds.getCenter()); map.innerMap.fitBounds(bounds); }); } } initMap();
CSS
html, body { height: 100%; margin: 0; } gmp-map { box-sizing: border-box; height: 500px; } gmp-place-list { background-color: #fff; font-size: large } .overlay { position: relative; top: 20px; margin: 20px; width: 400px; } .list-container { height: 400px; overflow: auto; border-radius: 10px; } /* Styles for the text search container and its elements */ .text-search-container { display: flex; /* Arrange input and button side-by-side */ gap: 8px; /* Space between input and button */ padding-bottom: 10px; /* Space below the search bar */ border-bottom: 1px solid #eee; /* Separator line */ } #textSearchInput { flex-grow: 1; /* Allow input to take available space */ padding: 8px 12px; border: 1px solid #ccc; border-radius: 4px; font-size: 1rem; } #textSearchInput:focus { outline: none; border-color: #4a80e8; /* Highlight on focus */ box-shadow: 0 0 0 2px rgba(74, 128, 232, 0.2); } #textSearchButton { padding: 8px 15px; background-color: #4a80e8; /* Example button color */ color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; transition: background-color 0.2s ease-in-out; } #textSearchButton:hover { background-color: #356ac0; /* Darker shade on hover */ }
HTML
<!DOCTYPE html> <html> <head> <title>Place List Text Search with Google Maps</title> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="style.css"> <script type="module" src="./index.js"></script> </head> <body> <gmp-map center="37.395641,-122.077627" zoom="10" map-id="DEMO_MAP_ID"> <div class="overlay" slot="control-inline-start-block-start"> <div class="text-search-container"> <input type="text" id="textSearchInput" placeholder="Enter text search..."> <button id="textSearchButton">Search</button> </div> <div class="list-container"> <gmp-place-list selectable style="display: none;"></gmp-place-list> </div> </div> <gmp-place-details style="display: none;"> <gmp-place-details-place-request></gmp-place-details-place-request> <gmp-place-all-content></gmp-place-all-content> </gmp-place-details> </gmp-map> <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: "alpha"});</script> </body> </html>