Add city autocomplete to a form
Show matching cities while a person types, and store the ID of the city that the person selects. The example uses a public key and plain JavaScript.
A free text field for the city gives you NYC, New York, and new york city for one city, and typos for many others. A city field with autocomplete gives you one ID for each city.
Create a public key
- On the dashboard, create a public key.
- Add the origin of your site, such as
https://www.example.com. - Add the origin of your local development server, such as
http://localhost:4321.
Keys and origins gives the rules for origins.
Request cities while the person types
Send the text of the field to Autocomplete. Autocomplete returns cities with a name that starts with the text. If no city matches a text of 4 or more characters, autocomplete also accepts a name with one typo.
Each successful response costs one unit, so do not send a request for each key press:
- Do not send a text of fewer than 2 characters. The API returns no cities for this text, and the response costs one unit.
- Wait for a short pause in the typing before you send the request.
- Cancel the previous request when the text changes, so that an old response does not replace a new one.
const AUTOCOMPLETE_URL = 'https://api.onde.earth/v1/cities/autocomplete'
const PUBLIC_KEY = 'onde_pk_...'
const MINIMUM_LENGTH = 2
const INPUT_DELAY_MS = 150
const input = form.elements.city
const cityId = form.elements.cityId
let timer
let controller
async function suggestCities(text, signal) {
const url = new URL(AUTOCOMPLETE_URL)
url.searchParams.set('q', text)
url.searchParams.set('key', PUBLIC_KEY)
const response = await fetch(url, { signal })
const body = await response.json()
if (!response.ok) {
throw new Error(body.detail)
}
return body.data
}
input.addEventListener('input', () => {
cityId.value = ''
clearTimeout(timer)
controller?.abort()
const text = input.value.trim()
if (text.length < MINIMUM_LENGTH) {
showCities([])
return
}
timer = setTimeout(async () => {
controller = new AbortController()
try {
showCities(await suggestCities(text, controller.signal))
} catch (error) {
if (error.name !== 'AbortError') {
showCities([])
}
}
}, INPUT_DELAY_MS)
}) form is your form, with a text field city and a hidden field cityId. showCities is a function of your interface that shows the list. If a request fails, the list stays empty, and the person can type the city as text.
Each change of the text clears cityId. Without this step, a person who selects Paris and then types Berlin sends the ID of Paris.
Show each city
Many cities have the same name. Show the region and the country with each name, so that the person can find the correct city.
function cityLabel(city) {
return [city.name, city.region?.name, city.country.name]
.filter(Boolean)
.join(', ')
} region is null for a city with no region. A result can also be a district, such as Manhattan. A district has type district, and parentCityId is the ID of its city.
Show the list with the combobox pattern of the WAI-ARIA Authoring Practices, so that a person can select a city with the keyboard. A combobox component of your framework can show the list.
Store the selected city
When the person selects a city, put its ID in the hidden field.
function selectCity(city) {
input.value = cityLabel(city)
cityId.value = city.id
showCities([])
} <input type="hidden" name="cityId"> On the server, send the ID to Get a city with a secret key. If the status is not 200, the ID is not valid, so ask the person to select the city again. The response also gives all fields of the city. Store cities in your database tells which fields to keep.
Accept text with no selection
A person can submit the form with no selection, for example when the API refused a request. Then cityId is empty, and city holds the text.
On the server, send the text to Search. Accept the first result only if its match is exact or alias, and no other result has exact or alias. Otherwise, store the text and ask the person to select the city later. Match city names from a file gives the same rule.
Set the language and the countries
These parameters of autocomplete change the results:
languagesets the language of the names. Use the language of the page.countrylimits the results to the countries in the list, such ascountry=PT,ES. Use it if your service is only in some countries.worldviewsets the country of a city in a disputed area. Worldviews and disputed areas tells how to select a worldview.