Maps SDK for JavaScript
Back to all examples
Map autocomplete fuzzy search playground
Combine autocomplete search with fuzzy search functionality
import { Places, TomTomConfig } from '@tomtom-org/maps-sdk/core'; import { BaseMapModule, PlacesModule, TomTomMap } from '@tomtom-org/maps-sdk/map'; import { type AutocompleteSearchBrandSegment, type AutocompleteSearchCategorySegment, type AutocompleteSearchResponse, type AutocompleteSearchResult, autocompleteSearch, search, } from '@tomtom-org/maps-sdk/services'; import './style.css'; import { API_KEY } from './config'; // (Set your own API key when working in your own environment) TomTomConfig.instance.put({ apiKey: API_KEY, language: 'en-GB' }); (async () => { const searchBox = document.getElementById('sdk-example-search-box') as HTMLInputElement; const autoCompleteResultsList = document.getElementById('sdk-example-autocompleteResults') as HTMLUListElement; const fuzzySearchResultsList = document.getElementById('sdk-example-fuzzySearchResults') as HTMLUListElement; const searchThisAreaButton = document.getElementById('sdk-example-search-this-area') as HTMLInputElement; const map = new TomTomMap({ container: 'sdk-map', center: [4.8156, 52.4414], zoom: 8 }); const placesModule = await PlacesModule.get(map); const baseMapModule = await BaseMapModule.get(map); fuzzySearchResultsList.addEventListener('mouseleave', () => { placesModule.cleanEventStates(); }); let selectedAutoCompleteSegment: | AutocompleteSearchBrandSegment | AutocompleteSearchCategorySegment | undefined | null; const clearFuzzySearchResults = () => { const searchResultsList = document.querySelector('#sdk-example-fuzzySearchResults') as HTMLUListElement; searchResultsList.innerHTML = ''; placesModule.clear(); searchThisAreaButton.innerHTML = ''; }; const showFuzzySearchResults = (places: Places) => { clearFuzzySearchResults(); if (selectedAutoCompleteSegment || !autoCompleteResultsList.childElementCount) { placesModule.show(places); } for (const place of places.features) { const resultItem = document.createElement('li'); resultItem.classList.add('sdk-example-result-item'); resultItem.dataset.placeId = place.id; if (place.properties.poi?.name) { resultItem.innerHTML = ` <a class="sdk-example-a"> <div class="sdk-example-result-value sdk-example-ellipsis">${place.properties.poi?.name}</div> <div class="sdk-example-result-value sdk-example-ellipsis">${place.properties.address.freeformAddress}</div> </a>`; } else { resultItem.innerHTML = ` <a class="sdk-example-a"> <div class="sdk-example-result-value sdk-example-ellipsis">${place.properties.address.freeformAddress}</div> </a>`; } // Add hover event listeners for list item resultItem.addEventListener('mouseenter', () => { placesModule.putEventState({ id: place.id, state: 'hover', mode: 'put' }); }); fuzzySearchResultsList.appendChild(resultItem); } if (!places.features.length) { const noResults = document.createElement('li'); noResults.classList.add('sdk-example-result-item'); noResults.innerHTML = `<div class="sdk-example-result-value sdk-example-ellipsis">No results found</div>`; fuzzySearchResultsList.appendChild(noResults); } }; const fuzzySearch = async () => { const searchParams = selectedAutoCompleteSegment ? { query: '', limit: 20, boundingBox: map.getBBox(), ...(selectedAutoCompleteSegment.type === 'category' && { poiCategories: [Number(selectedAutoCompleteSegment.id)], }), ...(selectedAutoCompleteSegment.type === 'brand' && { poiBrands: [selectedAutoCompleteSegment.value], }), } : { query: searchBox.value, typeahead: true, limit: 10, position: map.mapLibreMap.getCenter().toArray(), }; showFuzzySearchResults(await search(searchParams)); }; const createListElement = (result: AutocompleteSearchResult | null): HTMLElement => { const resultItem = document.createElement('li'); resultItem.classList.add('sdk-example-result-item'); if (!result) { resultItem.innerHTML = `<div class="sdk-example-result-value sdk-example-ellipsis">No results found</div>`; return resultItem; } const segment = result.segments[0] as AutocompleteSearchBrandSegment | AutocompleteSearchCategorySegment; resultItem.innerHTML = ` <a class="sdk-example-a"> <div class="sdk-example-result-value sdk-example-ellipsis">${segment.value}</div> <div class="sdk-example-result-type sdk-example-ellipsis">${segment.type}</div> </a> `; resultItem.addEventListener('click', async () => { selectedAutoCompleteSegment = segment; searchBox.value = segment.value; await fuzzySearch(); }); return resultItem; }; const clearAutoCompleteResults = () => (autoCompleteResultsList.innerHTML = ''); const showAutocompleteResults = (response: AutocompleteSearchResponse) => { clearAutoCompleteResults(); const brandsCategoryResults = response.results.filter((result) => result.segments[0].type !== 'plaintext'); if (brandsCategoryResults.length) { brandsCategoryResults.forEach((result) => { autoCompleteResultsList.appendChild(createListElement(result)); }); } else { // No results found: autoCompleteResultsList.appendChild(createListElement(null)); } }; const clearSearchResults = () => { clearAutoCompleteResults(); clearFuzzySearchResults(); }; const autoCompleteSearch = async () => { const autocompleteResponse = await autocompleteSearch({ query: searchBox.value, limit: 2 }); showAutocompleteResults(autocompleteResponse); }; const showSearchThisAreaButton = () => (searchThisAreaButton.innerHTML = `<button class="sdk-example-search-this-area-btn">Search This Area</button>`); map.mapLibreMap.on('moveend', () => { if (searchBox.value === selectedAutoCompleteSegment?.value) { showSearchThisAreaButton(); } }); searchThisAreaButton.addEventListener('click', fuzzySearch); const unhoverListItem = () => fuzzySearchResultsList.querySelector('.hovered')?.classList.remove('hovered'); placesModule.events.on('hover', (place) => { unhoverListItem(); const listItem = fuzzySearchResultsList.querySelector(`li[data-place-id="${place.id}"]`); listItem?.classList.add('hovered'); }); // Clean up list item hover when hovering outside pins baseMapModule.events.on('hover', () => { unhoverListItem(); placesModule.cleanEventStates(); }); searchBox.addEventListener('keyup', async () => { selectedAutoCompleteSegment = null; if (searchBox.value !== '') { await Promise.all([await autoCompleteSearch(), await fuzzySearch()]); } searchBox.value.trim() === '' && clearSearchResults(); }); })();
Related examples
Map ev charging stations playground
EV charging station discovery with availability data
Playground
Places and Search
Electric Vehicles
Web
Geometry search playground
Search for places within specific geographic boundaries
Playground
Places and Search
Geometry
Web
Geometry search with POI categories
Geometry search combined with POI category filtering
Playground
Places and Search
Geometry
Web