SearchBox integration

Overview of the tutorial

This tutorial shows the basic use of the SearchBox plugin to display it in a web page.

Prerequisites

To start using the latest version of the SearchBox plugin, you need the following:

We recommend that you have npm and Node.js already installed on your computer to quickly spin up a HTTP server to display the page we will create.

There are two ways to include the SearchBox into your page.

  • Using the SearchBox as an independent element.
  • Using the SearchBox as a control in the map.

Using the SearchBox as an independent element

Now, let's create a basic HTML file to display the SearchBox plugin. For this example we created a file named index.html.

Next, copy the following script to your index.html file.

1<!DOCTYPE html>
2<html class="use-all-space">
3 <head>
4 <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
5 <meta charset="UTF-8" />
6 <meta
7 name="viewport"
8 content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"
9 />
10 <title>SearchBox</title>
11 <link
12 rel="stylesheet"
13 type="text/css"
14 href="https://api.tomtom.com/maps-sdk-for-web/cdn/plugins/SearchBox/3.1.3-public-preview.0/SearchBox.css"
15 />
16 <script src="https://api.tomtom.com/maps-sdk-for-web/cdn/6.x/6.1.2-public-preview.15/services/services-web.min.js"></script>
17 <script src="https://api.tomtom.com/maps-sdk-for-web/cdn/plugins/SearchBox/3.1.3-public-preview.0/SearchBox-web.js"></script>
18 </head>
19 <body>
20 <script>
21 var options = {
22 searchOptions: {
23 key: "<your-tomtom-API-key>",
24 language: "en-GB",
25 limit: 5,
26 },
27 autocompleteOptions: {
28 key: "<your-tomtom-API-key>",
29 language: "en-GB",
30 },
31 }
32 var ttSearchBox = new tt.plugins.SearchBox(tt.services, options)
33 var searchBoxHTML = ttSearchBox.getSearchBoxHTML()
34 document.body.append(searchBoxHTML)
35 </script>
36 </body>
37</html>

Let's go over the important lines

First, we import all the necessary assets. In this case there are SearchBox styles, the plugin, and the Services library.

1<link
2 rel="stylesheet"
3 type="text/css"
4 href="https://api.tomtom.com/maps-sdk-for-web/cdn/plugins/SearchBox/3.1.3-public-preview.0/SearchBox.css"
5/>
6<script src="https://api.tomtom.com/maps-sdk-for-web/cdn/plugins/SearchBox/3.1.3-public-preview.0/SearchBox-web.js"></script>
7<script src="https://api.tomtom.com/maps-sdk-for-web/cdn/6.x/6.1.2-public-preview.15/services/services-web.min.js"></script>
  • SearchBox styles - these provide the styles for SearchBox.
  • SearchBox plugin - this provides the search box and its functionality to your page.
  • Services library - This provides the necessary tools to integrate Tomtom services with your application.

Then we define the basic options configuration. See the following code example.

1var options = {
2 searchOptions: {
3 key: "<your-tomtom-API-key>",
4 language: "en-GB",
5 limit: 5,
6 },
7 autocompleteOptions: {
8 key: "<your-tomtom-API-key>",
9 language: "en-GB",
10 },
11}
  • searchOptions - this is for the Fuzzy Search service. For a full list of options please refer to the Fuzzy Search service documentation.
  • autocompleteOptions - this is for the Autocomplete service. For a full list of options please refer to the Autocomplete service documentation.
    If you pass this option, the results list will be extended by the results returned by the Autocomplete service. They are used as filters for the fuzzy search.

Remember to replace the placeholders with your real data.

your-tomtom-API-KeyYour TomTom API Key generated earlier.

For more details on how to use the configuration options, please refer to the documentation.

Then, we can create our new SearchBox instance

var ttSearchBox = new tt.plugins.SearchBox(tt.services, options)

and append it to our application. In our example it's the body of the document.

var searchBoxHTML = ttSearchBox.getSearchBoxHTML()
document.body.appendChild(searchBoxHTML)

Using the SearchBox as a control in the map

The workflow of adding the SearchBox as a control is almost the same. You need to include a Map in your page and add there created instance of SearchBox with subscribed event handlers.

To check how to add the Map, please refer to the Display a vector map tutorial.

1var map = tt.map({
2 key: "<your-tomtom-API-Key>",
3 container: "map",
4 center: [15.4, 53.0],
5 zoom: 3,
6})
7var ttSearchBox = new tt.plugins.SearchBox(tt.services, options)
8var searchMarkersManager = new SearchMarkersManager(map)
9ttSearchBox.on("tomtom.searchbox.resultsfound", handleResultsFound)
10ttSearchBox.on("tomtom.searchbox.resultselected", handleResultSelection)
11ttSearchBox.on("tomtom.searchbox.resultfocused", handleResultSelection)
12ttSearchBox.on("tomtom.searchbox.resultscleared", handleResultClearing)
13map.addControl(ttSearchBox, "top-left")

SearchBox options was described above.

Remember to replace the placeholders with your real data.

your-tomtom-API-KeyYour TomTom API Key generated earlier.

For more map configuration options please refer to the Map documentation.

Then we define event handlers.

1function handleResultsFound(event) {
2 var results = event.data.results.fuzzySearch.results
3
4 if (results.length === 0) {
5 searchMarkersManager.clear()
6 }
7 searchMarkersManager.draw(results)
8 fitToViewport(results)
9}
10
11function handleResultSelection(event) {
12 var result = event.data.result
13 if (result.type === "category" || result.type === "brand") {
14 return
15 }
16 searchMarkersManager.draw([result])
17 fitToViewport(result)
18}
19
20function fitToViewport(markerData) {
21 if (!markerData || (markerData instanceof Array && !markerData.length)) {
22 return
23 }
24 var bounds = new tt.LngLatBounds()
25 if (markerData instanceof Array) {
26 markerData.forEach(function (marker) {
27 bounds.extend(getBounds(marker))
28 })
29 } else {
30 bounds.extend(getBounds(markerData))
31 }
32 map.fitBounds(bounds, { padding: 100, linear: true })
33}
34
35function getBounds(data) {
36 var btmRight
37 var topLeft
38 if (data.viewport) {
39 btmRight = [
40 data.viewport.btmRightPoint.lng,
41 data.viewport.btmRightPoint.lat,
42 ]
43 topLeft = [data.viewport.topLeftPoint.lng, data.viewport.topLeftPoint.lat]
44 }
45 return [btmRight, topLeft]
46}
47
48function handleResultClearing() {
49 searchMarkersManager.clear()
50}
  • handleResultsFound - executes when the search results are found. The event handler clears previously founded results and draws new. After that, it will try to fit drawn results on a vewport by executing fitToViewport method.
  • handleResultSelection - executes in two cases:
    • results were found and a user presses arrow up/down;
    • results were found and a user chooses one by clicking on it;
  • handleResultClearing - executes when a user clicks on "X" button of the SearchBox. As a result, all founded results will be cleared from the map.

After all these predefined steps we can create SearchMarkersManager, which will be responsible for manipulation with a marker.

In our example it has draw and clear methods

1function SearchMarkersManager(map, options) {
2 this.map = map
3 this._options = options || {}
4 this._poiList = undefined
5 this.markers = {}
6}
7
8SearchMarkersManager.prototype.draw = function (poiList) {
9 this._poiList = poiList
10 this.clear()
11 this._poiList.forEach(function (poi) {
12 var markerId = poi.id
13 var poiOpts = {
14 name: poi.poi ? poi.poi.name : undefined,
15 address: poi.address ? poi.address.freeformAddress : "",
16 distance: poi.dist,
17 classification: poi.poi ? poi.poi.classifications[0].code : undefined,
18 position: poi.position,
19 entryPoints: poi.entryPoints,
20 }
21 var marker = new SearchMarker(poiOpts, this._options)
22 marker.addTo(this.map)
23 this.markers[markerId] = marker
24 }, this)
25}
26
27SearchMarkersManager.prototype.clear = function () {
28 for (var markerId in this.markers) {
29 var marker = this.markers[markerId]
30 marker.remove()
31 }
32 this.markers = {}
33 this._lastClickedMarker = null
34}

and SearchMarker, which will be responsible for appearance of the marker and providing possibility add/remove it from the map

1function SearchMarker(poiData, options) {
2 this.poiData = poiData
3 this.options = options || {}
4 this.marker = new tt.Marker({
5 element: this.createMarker(),
6 anchor: "bottom",
7 })
8 var lon = this.poiData.position.lng || this.poiData.position.lon
9 this.marker.setLngLat([lon, this.poiData.position.lat])
10}
11
12SearchMarker.prototype.addTo = function (map) {
13 this.marker.addTo(map)
14 this._map = map
15 return this
16}
17
18SearchMarker.prototype.createMarker = function () {
19 var elem = document.createElement("div")
20 elem.className = "tt-icon-marker-black tt-search-marker"
21 if (this.options.markerClassName) {
22 elem.className += " " + this.options.markerClassName
23 }
24 var innerElem = document.createElement("div")
25 innerElem.setAttribute(
26 "style",
27 "background: white; width: 10px; height: 10px; border-radius: 50%; border: 3px solid black;"
28 )
29
30 elem.appendChild(innerElem)
31 return elem
32}
33
34SearchMarker.prototype.remove = function () {
35 this.marker.remove()
36 this._map = null
37}

For more advanced example of using the described code above please refer to the code tab of Search with autocomplete example.

Handling events

The SearchBox emits 7 different events:

  • tomtom.searchbox.inputrestored - fired when the input is restored. This happens when the user uses the arrows to navigate through the suggestions list and navigates back to the input.
  • tomtom.searchbox.loadingstarted - fired when a request in the background is started.
  • tomtom.searchbox.loadingfinished - fired when a request in the background is finished.
  • tomtom.searchbox.resultfocused - fired when the result is focused.
  • tomtom.searchbox.resultscleared - fired when the search results are cleared.
  • tomtom.searchbox.resultselected - fired when the user selects an element on the results list.
  • tomtom.searchbox.resultsfound - fired when the search results are found.

If you want to subscribe for an event, use the following example code:

1ttSearchBox.on("tomtom.searchbox.resultsfound", function (data) {
2 console.log(data)
3})

Option updating

If you have initialized the SearchBox and you want to change it's options later, you can do the following:

1ttSearchBox.updateOptions({
2 minNumberOfCharacters: 5,
3 showSearchButton: false,
4 labels: {
5 placeholder: "Query e.g. TomTom",
6 },
7})

The query method triggers the search with updated options. This is necessary if you want to update results for the user with new options.

ttSearchBox.query()

For more details on how to use the configuration options, please refer to the documentation.

Run a HTTP server

Using a terminal, navigate to the directory containing our example.

For example:

~$ cd searchbox-example/
~searchbox-example$

Install a lightweight HTTP server (you might need to run this command with admin/root privileges).

npm install -g http-server

Then just simply run the following command.

http-server

Note that npm comes bundled with Node.js, so please make sure you have it installed on your machine.

So now it is time to see the results!

Open a browser and type this URL in the address bar: http://localhost:8080/index.html. You should be able to see the SearchBox!

vector-map

Summary

From this tutorial you learned:

  • How to prepare a basic HTML page to use the SearchBox.
  • How to show the SearchBox on your map.