Widget
The drop-in dropdown component, every option, events, styling, accessibility and a full example
widget.js attaches an address dropdown to an input you already have and fills the form fields
when the user picks a suggestion. It follows the client rules
for you: debounce, minimum length, session handling, stale responses and attribution. It has no
dependencies. If you want your own dropdown instead, see Build your own UI.
Add it
The script is served by the service itself. Where the Umbrella gateway exposes it, and how the
widget authenticates through the gateway, is pending verification; it is written as BASE_URL.
<script src="BASE_URL/widget.js"></script>
<script>
AddressAutocomplete.attach({
input: '#address',
apiKey: 'BROWSER_KEY',
countries: ['DE'],
fields: {
street: '#street',
house_number: '#nr',
postal_code: '#plz',
city: '#city',
country_code: '#cc',
},
onSelect: function (address, suggestion, response) {
// address is the resolved address; response is the full resolve payload
},
});
</script>attach throws AddressAutocomplete: input not found if the selector matches nothing, so call it after
the input exists (end of body, or on DOMContentLoaded).
Options
| Option | Default | Notes |
|---|---|---|
input | none | Element or selector. Required |
apiKey | none | Sent as the X-API-Key header. Required on a live service, not needed against Mock |
endpoint | directory of the script | Base URL of the service, without a trailing slash. The widget appends /v1/predict and /v1/resolve. If the script is loaded in a way that leaves no script element to read (for example as a module), set it explicitly |
countries | service default | Array of one or more country codes to restrict results to, for example ['DE'] or ['DE', 'FR']. See Country and address type |
near | none | {lat, lng} to bias results toward a point. Send it only if the user has agreed to share a position |
minChars | 3 | Characters before the first call. Below the service minimum a lower value only wastes calls |
debounceMs | 250 | Wait after the last keystroke before calling predict |
fields | none | Map from an address field name to a selector, filled on select. Any address field can be used: formatted, street, house_number, postal_code, city, district, state, country, country_code, lat, lng, precision |
onSelect(address, suggestion, response) | none | Called after a successful resolve. address is response.address; response.verification is set when verification is enabled |
onError(err) | none | Called on a failed predict or resolve |
crosscheck | service setting | A boolean the widget passes on to resolve. The service honours it only in demo setups; in production the setting is service-wide, so leave it out |
The widget does not send language; the service default (de) applies. It also does not expose a way to
set session, which it manages itself.
attach returns a handle:
| Method | Does |
|---|---|
close() | Closes the list |
destroy() | Closes the list, removes the wrapper and replaces the input with a copy of itself so the widget's listeners are gone. Any reference you hold to the old input element is stale afterwards, and the copy keeps the role and aria-* attributes the widget set |
Events and callbacks
On a successful pick, in this order:
- The input is set to the suggestion's
textand the list closes. - The widget calls
resolve. If it fails,onErroris called, no field is filled and no event fires. - The
fieldstargets are filled (anullvalue, such as a missinglat, becomes an empty string). onSelect(address, suggestion, response)is called.- A bubbling
address:selectedCustomEventis dispatched on the input. Itsdetailis the full resolve response,{address, verification}, not the bare address.
document.querySelector('#address').addEventListener('address:selected', function (e) {
console.log(e.detail.address.formatted, e.detail.address.precision);
});The widget sets values programmatically and does not dispatch input or change events on the input or on
the fields targets. A framework that tracks form state through those events (React controlled inputs, Vue
v-model) will not see the new values. Read them in onSelect and update your own state there.
suggestion is the object the user picked: {id, text, main_text, secondary_text}.
Always check address.precision. A street-level pick has no house_number; the user still has to complete it.
Behaviour
- Calls. After the debounce, and when the trimmed text is at least
minCharslong, it sendsPOST {endpoint}/v1/predictwithinput,session,countriesandnear. On a pick it sendsPOST {endpoint}/v1/resolvewithidandsession. Both are JSON withContent-Type: application/jsonand theX-API-Keyheader whenapiKeyis set. - Session. The widget takes
sessionfrom eachpredictresponse and sends it on later calls. After a successfulresolveit drops it, so the next address entry starts a new session. It does not drop it when the user clears the field without picking, so keystrokes of an abandoned entry and the next entry share one session. - Stale responses. Each
predictgets a sequence number and a response that is no longer the latest is ignored. Requests are not cancelled, only ignored. One gap: deleting text belowminCharscloses the list but does not invalidate a request already in flight, so its response can reopen the list. - Empty results. The list stays closed and the field stays editable. There is no "no results" message.
- Attribution. A non-empty
attributionfrom the API is rendered as the last row of the list. Keep it visible; it is a licence obligation. - Closing. Esc closes the list, and so does leaving the field (after 150 ms, so a click on a suggestion still registers).
- Several fields. You can call
attachonce per input. The stylesheet is injected once.
Accessibility
The widget sets the ARIA combobox pattern: role="combobox", aria-autocomplete="list", aria-expanded and
aria-controls on the input, aria-activedescendant pointing at the highlighted option, role="listbox" on
the list and role="option" with aria-selected on each suggestion. The input gets autocomplete="off".
| Key | Action |
|---|---|
| Down, Up | Move the highlight through the suggestions. It does not wrap, and Up from the field goes to the first item |
| Enter | Picks the highlighted suggestion. With nothing highlighted the key is left alone, so the form submits as usual |
| Esc | Closes the list |
Not provided: a live region that announces the number of suggestions, a "no results" message, and a minimum
touch-target size beyond the item padding (8 px vertical). If your accessibility requirements ask for these,
add a polite live region next to the input and set a larger padding on .idc-ac-item. The highlight is a
background colour only; add a second cue if you need one. Pending verification: no screen reader test was
done for this page.
Styling and theming
There are no theme options or CSS variables. The widget injects one <style id="idc-ac-css"> with minimal
rules and everything is overridden with ordinary CSS on these classes:
| Class | Element |
|---|---|
.idc-ac-wrap | A div the widget puts around your input, position: relative |
.idc-ac-list | The ul dropdown, absolutely positioned under the input, z-index: 1000, white background |
.idc-ac-item | One suggestion (li) |
.idc-ac-item[aria-selected=true] | The highlighted suggestion |
.idc-ac-main, .idc-ac-sec | The first line (street and number) and the second line (postcode, city, country) |
.idc-ac-attr | The attribution row |
.idc-ac-list { background: #1b1f27; border-color: #3a4150; color: #e8ebf0; }
.idc-ac-item[aria-selected=true], .idc-ac-item:hover { background: #2b3a55; }
.idc-ac-attr { color: #9aa4b2; }Because the widget wraps your input in a block-level div, an input that sits inline in a flex row needs the
wrapper styled instead (for example .idc-ac-wrap { flex: 1; }).
Browser keys, CORS and CSP
The widget calls the API from the browser, so you need a key of your own and your site's origin on the service's allowed-origin list (a service-wide list, not per key). A browser key is visible to anyone on the page, and origin checks do not stop scripts. If that is not acceptable, proxy the service calls through your backend and use the API directly.
- CORS. The calls are cross-origin
POSTrequests with a JSON body and a custom header, so the browser sends a preflight first. The service allowsGETandPOSTand any request header for the origins on its list. A missing origin shows up in the console as a CORS error, not as an HTTP error the widget can report. - CSP. If your site sets a Content Security Policy, allow the service origin in
script-src(forwidget.js) and inconnect-src(for the API calls). The widget also injects an inline<style>element and has no nonce support, so astyle-srcwithout'unsafe-inline'blocks that stylesheet. In that case copy the rules above into your own stylesheet; the widget works without its injected styles. Not tested under a strict policy. - No pinned version. The script is served as one file with no version in the URL, and it can change when the service does. Pending verification: whether a versioned URL will be offered.
Pointing it at Mock, sandbox and production
| Target | endpoint | apiKey | Script |
|---|---|---|---|
| Mock (German and Austrian sample data only) | https://<this portal>/mock/api/services/address/autocomplete | not needed | https://<this portal>/mock/address-autocomplete/widget.js |
| Sandbox | the Umbrella sandbox base URL from Environments plus the gateway sub-path | your key | Pending verification |
| Production | the Umbrella production base URL plus the gateway sub-path | your key | Pending verification |
The gateway sub-path (/address/autocomplete), the way the gateway expects the key (the widget sends
X-API-Key, the rest of Umbrella uses a bearer token) and the script URL are pending verification.
Full example
A complete page against Mock. Replace <this portal> with the address of the site you are reading this on. Mock
holds invented German and Austrian addresses only (the sample, not the coverage of the service): type Haupt or Bahnhofstr 12 Wien.
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Address entry</title>
<style>
form { max-width: 28rem; font: 16px/1.4 system-ui, sans-serif; }
label { display: block; margin-top: .75rem; }
input { width: 100%; box-sizing: border-box; padding: .5rem; }
.idc-ac-item { min-height: 44px; display: flex; flex-direction: column; justify-content: center; }
#status { margin-top: .75rem; min-height: 1.4em; }
</style>
</head>
<body>
<form id="f" autocomplete="off">
<label>Address <input id="address" type="text"></label>
<label>Street <input id="street" type="text"></label>
<label>Number <input id="nr" type="text"></label>
<label>Postcode <input id="plz" type="text"></label>
<label>City <input id="city" type="text"></label>
<label>Country <input id="cc" type="text"></label>
<p id="status" role="status" aria-live="polite"></p>
</form>
<script src="https://<this portal>/mock/address-autocomplete/widget.js"></script>
<script>
var statusEl = document.getElementById('status');
AddressAutocomplete.attach({
input: '#address',
endpoint: 'https://<this portal>/mock/api/services/address/autocomplete',
countries: ['DE'],
fields: {
street: '#street',
house_number: '#nr',
postal_code: '#plz',
city: '#city',
country_code: '#cc',
},
onSelect: function (address) {
statusEl.textContent = address.precision === 'house'
? ''
: 'Please add the house number.';
if (address.precision !== 'house') document.getElementById('nr').focus();
},
onError: function (err) {
// err.message is 'HTTP <status>' for an API error, or the browser's message for a network or CORS failure
statusEl.textContent = 'Address suggestions are unavailable. You can type the address by hand.';
},
});
</script>
</body>
</html>The role="status" paragraph is not part of the widget; it is how this page tells screen-reader users about
a missing house number or an error.
Errors the widget reports
onError receives an Error. For an API error its message is HTTP <status>; there is no status
property and the response body is not passed on. The statuses are the ones in the
integration guide: 401 is a configuration
problem (key), 404 on resolve means the suggestion expired, 422 means a country that is not enabled or a
bad field, 502 means the address source is unavailable. In every case keep the field editable and let the
user type the address by hand. A failed predict closes the list; the next keystroke tries again.
Pending verification: the widget behaviour above is taken from its source and has not been run against the live service.