Phần tử Tìm kiếm địa điểm

Chọn nền tảng: Android iOS JavaScript

PlaceSearchElement là một phần tử HTML hiển thị kết quả của một lượt tìm kiếm địa điểm trong danh sách. Có hai cách để định cấu hình phần tử gmp-place-search:

Tìm kiếm yêu cầu lân cận

Chọn một loại địa điểm trong trình đơn để xem kết quả tìm kiếm lân cận cho loại địa điểm đó.

Tính năng tìm kiếm lân cận chủ yếu được định cấu hình để tìm kiếm theo loại địa điểm và vị trí, đồng thời kết quả có thể được xếp hạng theo khoảng cách hoặc mức độ phổ biến bằng cách sử dụng thuộc tính rankPreference. Hãy xem tài liệu tham khảo về lớp PlaceNearbySearchRequestElement để biết thêm thông tin chi tiết.

Ví dụ này kết xuất phần tử Tìm kiếm địa điểm để phản hồi một cụm từ tìm kiếm ở gần với loại địa điểm do người dùng chọn. Thao tác này cũng hiển thị một PlaceDetailsCompactElement cho địa điểm đã chọn.

Xem ví dụ đầy đủ về mã

Để thêm phần tử Tìm kiếm địa điểm vào bản đồ, hãy thêm phần tử gmp-place-search có phần tử gmp-place-nearby-search-request lồng ghép vào trang HTML.

<gmp-place-search selectable>
    <gmp-place-all-content></gmp-place-all-content>
    <gmp-place-nearby-search-request
        max-result-count="5"></gmp-place-nearby-search-request>
</gmp-place-search>

Phần tử select cho phép người dùng chọn một loại địa điểm trong trình đơn. Để đơn giản, chỉ có 3 loại địa điểm được liệt kê: nhà hàng, quán cà phê và trạm sạc xe điện.

<div class="controls">
    <label for="type-select">
        Select a place type:
        <select id="type-select" class="type-select">
            <option value="restaurant">Restaurant</option>
            <option value="cafe" selected>Cafe</option>
            <option value="electric_vehicle_charging_station">
                EV charging station
            </option>
        </select>
    </label>
</div>

Khi người dùng chọn một loại địa điểm trong trình đơn, phần tử gmp-place-nearby-search-request sẽ được cập nhật và phần tử Tìm kiếm địa điểm sẽ hiển thị kết quả, như trong các đoạn mã sau. Các điểm đánh dấu được thêm vào hàm trợ giúp addMarkers.

// Add event listeners to the type select and place search elements.
    typeSelect.addEventListener('change', () => searchPlaces());
    placeSearch.addEventListener('gmp-select', (event) => {
        const { place } = event;
        markers.get(place.id)?.click();
    });
    placeSearch.addEventListener('gmp-load', () => {
        addMarkers();
    });
    searchPlaces();
}
// The searchPlaces function is called when the user changes the type select or when the page loads.
async function searchPlaces() {
    // Close the info window and clear the markers.
    infoWindow.close();
    for (const marker of markers.values()) {
        marker.remove();
    }
    markers.clear();
    // Set the place search query and add an event listener to the place search element.
    if (typeSelect.value) {
        const center = map.center;
        placeSearchQuery.locationRestriction = {
            center,
            radius: 50000, // 50km radius
        };
        placeSearchQuery.locationBias = {
            center,
        };
        placeSearchQuery.includedTypes = [typeSelect.value];
    }
}

Ví dụ đầy đủ về mã

JavaScript

// Query selectors for various elements in the HTML file.
const map = document.querySelector('gmp-map');
const placeSearch = document.querySelector('gmp-place-search');
const placeSearchQuery = document.querySelector('gmp-place-nearby-search-request');
const placeDetails = document.querySelector('gmp-place-details-compact');
const placeRequest = document.querySelector('gmp-place-details-place-request');
const typeSelect = document.querySelector('.type-select');
// Global variables for the map, markers, and info window.
const markers = new Map();
let infoWindow;
// The init function is called when the page loads.
async function init() {
    // Import the necessary libraries from the Google Maps API.
    const [{ InfoWindow }, { Place }] = await Promise.all([
        google.maps.importLibrary('maps'),
        google.maps.importLibrary('places'),
    ]);
    // Create a new info window and set its content to the place details element.
    placeDetails.remove(); // Hide the place details element because it is not needed until the info window opens
    infoWindow = new InfoWindow({
        content: placeDetails,
        ariaLabel: 'Place Details',
    });
    // Set the map options.
    map.innerMap.setOptions({
        clickableIcons: false,
        mapTypeControl: false,
        streetViewControl: false,
    });
    // Add event listeners to the type select and place search elements.
    typeSelect.addEventListener('change', () => searchPlaces());
    placeSearch.addEventListener('gmp-select', (event) => {
        const { place } = event;
        markers.get(place.id)?.click();
    });
    placeSearch.addEventListener('gmp-load', () => {
        addMarkers();
    });
    searchPlaces();
}
// The searchPlaces function is called when the user changes the type select or when the page loads.
async function searchPlaces() {
    // Close the info window and clear the markers.
    infoWindow.close();
    for (const marker of markers.values()) {
        marker.remove();
    }
    markers.clear();
    // Set the place search query and add an event listener to the place search element.
    if (typeSelect.value) {
        const center = map.center;
        placeSearchQuery.locationRestriction = {
            center,
            radius: 50000, // 50km radius
        };
        placeSearchQuery.locationBias = {
            center,
        };
        placeSearchQuery.includedTypes = [typeSelect.value];
    }
}
// The addMarkers function is called when the place search element loads.
async function addMarkers() {
    // Import the necessary libraries from the Google Maps API.
    const [{ AdvancedMarkerElement }, { LatLngBounds }] = await Promise.all([
        google.maps.importLibrary('marker'),
        google.maps.importLibrary('core'),
    ]);
    const bounds = new LatLngBounds();
    if (placeSearch.places.length === 0) {
        return;
    }
    for (const place of placeSearch.places) {
        const marker = new AdvancedMarkerElement({
            map: map.innerMap,
            position: place.location,
            collisionBehavior: google.maps.CollisionBehavior.REQUIRED_AND_HIDES_OPTIONAL,
        });
        markers.set(place.id, marker);
        bounds.extend(place.location);
        marker.addListener('click', () => {
            placeRequest.place = place;
            infoWindow.open(map.innerMap, marker);
        });
    }
    map.innerMap.fitBounds(bounds);
}
init();

CSS

html,
body {
    height: 100%;
    margin: 0;
}

body {
    display: flex;
    flex-direction: column;
    font-family: Arial, Helvetica, sans-serif;
}

.container {
    display: flex;
    height: 100vh;
    width: 100%;
}

gmp-map {
    flex-grow: 1;
}

.ui-panel {
    width: 400px;
    margin-left: 20px;
    margin-top: 10px;
    overflow-y: auto;
    font-family: Arial, Helvetica, sans-serif;
}

.list-container {
    display: flex;
    flex-direction: column;
}

gmp-place-search {
    width: 100%;
    margin: 0;
    border: none;
    color-scheme: light;
}

HTML

<!doctype html>
<html>
    <head>
        <title>Place Search Nearby with Google Maps</title>
        <meta charset="utf-8" />
        <link rel="stylesheet" type="text/css" href="style.css" />
        <script type="module" src="./index.js" defer></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>

        <div class="container">
            <!-- map-id is required to use advanced markers. See https://developers.google.com/maps/documentation/javascript/map-ids/mapid-over. -->
            <gmp-map center="-37.813,144.963" zoom="16" map-id="DEMO_MAP_ID">
            </gmp-map>
            <div class="ui-panel">
                <div class="controls">
                    <label for="type-select">
                        Select a place type:
                        <select id="type-select" class="type-select">
                            <option value="restaurant">Restaurant</option>
                            <option value="cafe" selected>Cafe</option>
                            <option value="electric_vehicle_charging_station">
                                EV charging station
                            </option>
                        </select>
                    </label>
                </div>
                <div class="list-container">
                    <gmp-place-search selectable>
                        <gmp-place-all-content></gmp-place-all-content>
                        <gmp-place-nearby-search-request
                            max-result-count="5"></gmp-place-nearby-search-request>
                    </gmp-place-search>
                </div>
            </div>
        </div>

        <!--
        The gmp-place-details-compact element is styled inline because it is
        conditionally rendered and moved into the info window, which is
        part of the map's shadow DOM.
    -->
        <gmp-place-details-compact
            orientation="horizontal"
            truncation-preferred
            style="
                width: 400px;
                padding: 0;
                margin: 0;
                border: none;
                background-color: transparent;
                color-scheme: light;
            ">
            <gmp-place-details-place-request></gmp-place-details-place-request>
            <gmp-place-content-config>
                <gmp-place-media></gmp-place-media>
                <gmp-place-rating></gmp-place-rating>
                <gmp-place-price></gmp-place-price>
                <gmp-place-accessible-entrance-icon></gmp-place-accessible-entrance-icon>
                <gmp-place-open-now-status></gmp-place-open-now-status>
                <gmp-place-attribution
                    light-scheme-color="gray"
                    dark-scheme-color="white"></gmp-place-attribution>
            </gmp-place-content-config>
        </gmp-place-details-compact>

    </body>
</html>

Dùng thử mẫu

Tìm kiếm bằng yêu cầu văn bản

Nhập một cụm từ tìm kiếm vào trường nhập liệu rồi nhấp vào nút Tìm kiếm để xem danh sách những địa điểm phù hợp với cụm từ đó.

Tính năng tìm kiếm bằng văn bản được định cấu hình chủ yếu để tìm kiếm bằng cụm từ tìm kiếm và vị trí, đồng thời bạn có thể tinh chỉnh kết quả theo mức giá, điểm xếp hạng và trạng thái hiện tại (đang mở cửa hay không). Bạn cũng có thể xếp hạng kết quả theo khoảng cách hoặc mức độ phổ biến bằng cách sử dụng thuộc tính rankPreference. Hãy xem tài liệu tham khảo về lớp PlaceTextSearchRequestElement để biết thêm thông tin chi tiết.

Ví dụ này hiển thị phần tử Tìm kiếm địa điểm để phản hồi thao tác nhập văn bản của người dùng. Thao tác này cũng hiển thị một PlaceDetailsCompactElement cho địa điểm đã chọn.

Xem ví dụ đầy đủ về mã

Để thêm phần tử Tìm kiếm địa điểm vào bản đồ, hãy thêm phần tử gmp-place-search có phần tử gmp-place-search-text-search-request lồng ghép vào trang HTML.

<gmp-place-search selectable>
    <gmp-place-all-content></gmp-place-all-content>
    <gmp-place-text-search-request
        max-result-count="5"></gmp-place-nearby-search-request>
</gmp-place-search>

Phần tử input cho phép người dùng nhập văn bản tìm kiếm.

<div class="controls">
    <input
        type="text"
        id="query-input"
        class="query-input"
        placeholder="Search for a place"
        value="cafe" />
    <button id="search-button" class="search-button">
        Search
    </button>
</div>

Khi người dùng nhấp vào nút Tìm kiếm, hàm tìm kiếm sẽ chạy, phần tử gmp-place-text-search-request sẽ được cập nhật và phần tử Tìm kiếm địa điểm sẽ hiển thị kết quả như trong các đoạn mã sau. Các điểm đánh dấu được thêm vào hàm trợ giúp addMarkers.

// Add event listeners to the query input and place search elements.
    searchButton.addEventListener('click', () => searchPlaces());
    queryInput.addEventListener('keydown', (event) => {
        if (event.key === 'Enter') {
            searchPlaces();
        }
    });
    placeSearch.addEventListener('gmp-select', (event) => {
        const { place } = event;
        markers.get(place.id)?.click();
    });
    placeSearch.addEventListener('gmp-load', () => {
        addMarkers();
    });
    searchPlaces();
}
// The searchPlaces function is called when the user changes the query input or when the page loads.
async function searchPlaces() {
    // Close the info window and clear the markers.
    infoWindow.close();
    for (const marker of markers.values()) {
        marker.remove();
    }
    markers.clear();
    // Set the place search query and add an event listener to the place search element.
    if (queryInput.value) {
        const center = map.center;
        if (center) {
            placeSearchQuery.locationBias = center;
        }
        // The textQuery property is required for the search element to load.
        // Any other configured properties will be ignored if textQuery is not set.
        placeSearchQuery.textQuery = queryInput.value;
    }
}

Ví dụ đầy đủ về mã

JavaScript

// Query selectors for various elements in the HTML file.
const map = document.querySelector('gmp-map');
const placeSearch = document.querySelector('gmp-place-search');
const placeSearchQuery = document.querySelector('gmp-place-text-search-request');
const placeDetails = document.querySelector('gmp-place-details-compact');
const placeRequest = document.querySelector('gmp-place-details-place-request');
const queryInput = document.querySelector('.query-input');
const searchButton = document.querySelector('.search-button');
// Global variables for the map, markers, and info window.
const markers = new Map();
let infoWindow;
// The init function is called when the page loads.
async function init() {
    // Import the necessary libraries from the Google Maps API.
    const [{ InfoWindow }, { Place }] = await Promise.all([
        google.maps.importLibrary('maps'),
        google.maps.importLibrary('places'),
    ]);
    // Create a new info window and set its content to the place details element.
    placeDetails.remove(); // Hide the place details element because it is not needed until the info window opens
    infoWindow = new InfoWindow({
        content: placeDetails,
        ariaLabel: 'Place Details',
    });
    // Set the map options.
    map.innerMap.setOptions({
        clickableIcons: false,
        mapTypeControl: false,
        streetViewControl: false,
    });
    // Add event listeners to the query input and place search elements.
    searchButton.addEventListener('click', () => searchPlaces());
    queryInput.addEventListener('keydown', (event) => {
        if (event.key === 'Enter') {
            searchPlaces();
        }
    });
    placeSearch.addEventListener('gmp-select', (event) => {
        const { place } = event;
        markers.get(place.id)?.click();
    });
    placeSearch.addEventListener('gmp-load', () => {
        addMarkers();
    });
    searchPlaces();
}
// The searchPlaces function is called when the user changes the query input or when the page loads.
async function searchPlaces() {
    // Close the info window and clear the markers.
    infoWindow.close();
    for (const marker of markers.values()) {
        marker.remove();
    }
    markers.clear();
    // Set the place search query and add an event listener to the place search element.
    if (queryInput.value) {
        const center = map.center;
        if (center) {
            placeSearchQuery.locationBias = center;
        }
        // The textQuery property is required for the search element to load.
        // Any other configured properties will be ignored if textQuery is not set.
        placeSearchQuery.textQuery = queryInput.value;
    }
}
// The addMarkers function is called when the place search element loads.
async function addMarkers() {
    // Import the necessary libraries from the Google Maps API.
    const [{ AdvancedMarkerElement }, { LatLngBounds }] = await Promise.all([
        google.maps.importLibrary('marker'),
        google.maps.importLibrary('core'),
    ]);
    const bounds = new LatLngBounds();
    if (placeSearch.places.length === 0) {
        return;
    }
    for (const place of placeSearch.places) {
        const marker = new AdvancedMarkerElement({
            map: map.innerMap,
            position: place.location,
            collisionBehavior: google.maps.CollisionBehavior.REQUIRED_AND_HIDES_OPTIONAL,
        });
        markers.set(place.id, marker);
        bounds.extend(place.location);
        marker.addListener('click', () => {
            placeRequest.place = place;
            infoWindow.open(map.innerMap, marker);
        });
    }
    map.innerMap.fitBounds(bounds);
}
init();

CSS

html,
body {
    height: 100%;
    margin: 0;
}

body {
    display: flex;
    flex-direction: column;
    font-family: Arial, Helvetica, sans-serif;
}

.container {
    display: flex;
    height: 100vh;
    width: 100%;
}

gmp-map {
    flex-grow: 1;
}

.ui-panel {
    width: 400px;
    margin-left: 20px;
    margin-right: 20px;
    margin-top: 10px;
    overflow-y: auto;
    font-family: Arial, Helvetica, sans-serif;
}

.list-container {
    display: flex;
    flex-direction: column;
}

gmp-place-search {
    width: 100%;
    margin: 0;
    border: none;
    color-scheme: light;
}

.query-input {
    width: 100%;
    padding: 8px;
    margin-bottom: 10px;
    box-sizing: border-box;
}

.search-button {
    width: 100%;
    padding: 8px;
    margin-bottom: 10px;
    box-sizing: border-box;
    background-color: #1a73e8;
    color: white;
    border: none;
    cursor: pointer;
}

.search-button:hover,
.search-button:focus-visible {
    background-color: #1765cc;
}

HTML

<!DOCTYPE html>
<html>
    <head>
        <title>Place 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" defer></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>

        <div class="container">
            <div class="ui-panel">
                <div class="controls">
                    <input
                        type="text"
                        id="query-input"
                        class="query-input"
                        placeholder="Search for a place"
                        value="cafe" />
                    <button class="search-button">Search</button>
                </div>
                <div class="list-container">
                    <gmp-place-search selectable>
                        <gmp-place-all-content></gmp-place-all-content>
                        <gmp-place-text-search-request
                            max-result-count="5"></gmp-place-text-search-request>
                    </gmp-place-search>
                </div>
            </div>
            <!-- map-id is required to use advanced markers. See https://developers.google.com/maps/documentation/javascript/map-ids/mapid-over. -->
            <gmp-map center="-37.813,144.963" zoom="16" map-id="DEMO_MAP_ID">
            </gmp-map>
        </div>

        <!--
        The gmp-place-details-compact element is styled inline because it is
        conditionally rendered and moved into the info window, which is
        part of the map's shadow DOM.
        -->
        <gmp-place-details-compact
            orientation="horizontal"
            truncation-preferred
            style="
                width: 400px;
                padding: 0;
                margin: 0;
                border: none;
                background-color: transparent;
                color-scheme: light;
            ">
            <gmp-place-details-place-request></gmp-place-details-place-request>
            <gmp-place-content-config>
                <gmp-place-media></gmp-place-media>
                <gmp-place-rating></gmp-place-rating>
                <gmp-place-price></gmp-place-price>
                <gmp-place-accessible-entrance-icon></gmp-place-accessible-entrance-icon>
                <gmp-place-open-now-status></gmp-place-open-now-status>
                <gmp-place-attribution
                    light-scheme-color="gray"
                    dark-scheme-color="white"></gmp-place-attribution>
            </gmp-place-content-config>
        </gmp-place-details-compact>

    </body>
</html>

Dùng thử mẫu