Developers

Widget JavaScript API

Run a search, apply a filter or open a location from your own page script, with calls that queue until the locator is ready.

Drive the locator from your page

The <biz-locator> element the embed snippet creates is a normal custom element with methods on it. Your own page can run a search, apply a filter, open a location or read the current state, which is what a search bar in your site header, a product page with a "find it near me" button, or a region landing page needs.

Methods are the write side of the browser contract. The read side, the events the locator dispatches when a visitor acts, is on the widget events page.

JavaScript
const locator = document.querySelector("biz-locator");

await locator.searchNear("Portland, OR");
await locator.setFilters(["service-center"]);

console.log(locator.getState().resultCount);

Calls before the widget is ready

Every method except getState() returns a Promise, and every call made before the locator has loaded its data is queued and replayed, in order, once it has. A script that runs before the widget script, or before the embed snippet has created the element, does not need to wait for anything.

Two ways to get hold of the element:

  • Find it yourself with document.querySelector("biz-locator") once it exists on the page. The embed snippet creates it when the document has parsed.
  • Let the widget hand it to you. window.HandledLocator.ready() resolves with the first locator on the page once it is ready. This global exists as soon as the widget script has run.
Wait for the locator
window.HandledLocator.ready().then((locator) => {
locator.setFilters(["flagship"]);
});

A script that may run before the widget script itself has loaded can listen for the ready event on document instead. It bubbles, and its target is the element.

Before the widget script
document.addEventListener("handled:ready", (event) => {
const locator = event.target;
locator.searchNear({ lat: 45.52, lng: -122.68, label: "Portland" });
});

Method reference

  • search(text) sets the keyword search. It applies at once and involves no geocoding. Resolves when applied.
  • searchNear(place) searches near a place. Pass a string and it is geocoded exactly as if the visitor had typed it, which costs one geocoding request against your map provider key, or pass { lat, lng, label } and the coordinates are used as they are. Resolves true when the locator has a point to measure from and false when the place could not be found.
  • setFilters(tags) replaces the active filters. Pass tag names, slugs or ids in any mix; matching ignores case and punctuation, so "Service Center" and "service-center" reach the same tag. Resolves with the slugs that matched, so a typo shows up as a missing entry rather than a silent no-op.
  • clearFilters() removes every filter.
  • openLocation(id) opens one location the way a click on its card would: the detail pane or the map popup, depending on your card click setting, and the map focuses its pin. Resolves false when the id is not in the loaded set. Ids come from the Location API or from the handled:location-selected event.
  • closeLocation() closes the open pane or popup.
  • locate() asks the browser for the visitor's position and searches near it. Resolves true when the visitor allowed it. The browser prompt appears only inside this call, never on load, unless you have turned on automatic location in search settings.
  • reset() returns the locator to how it starts: no search, no filters, your default open-now view.
  • getState() returns the current state synchronously. Before the locator is ready every field is a placeholder and ready is false.
  • setLanguage(code) shows the interface in one of the account's languages. Resolves with the language that matched, fr for fr-CA when only fr exists, or null when none did. Pass null to go back to matching the visitor's browser. Useful on a site with its own language switcher.
  • getLocations() resolves with the locations currently listed, in list order, each with a distanceKm from the search point when there is one. getLocations({ all: true }) returns every location the embed loaded. The records are the same public fields the locator itself is served.
State shape
{
ready: true,
query: "",
near: { lat: 45.52, lng: -122.68, label: "Portland, Oregon" } | null,
radius: 25,           // in your display units, 0 for any distance
filters: ["service-center"],
openLocationId: "…" | null,
resultCount: 12,
locationCount: 240
}

Worked examples

A search bar in your site header. Read the visitor's text and hand it to the locator on the same page. For a search bar on a different page, use a plain form and the URL parameters instead; that recipe is the external search bar guide.

Same-page search bar
document.querySelector("#header-search").addEventListener("submit", async (event) => {
event.preventDefault();
const locator = await window.HandledLocator.ready();
const found = await locator.searchNear(event.target.elements.place.value);
if (!found) alert("We could not find that place. Try a town or postcode.");
});

A product page. Filter to the retailers that carry this product, then search near the visitor.

Find this product near me
document.querySelector("#find-near-me").addEventListener("click", async () => {
const locator = await window.HandledLocator.ready();
await locator.setFilters(["habanero-reserve"]);
await locator.locate();
});

A region landing page. Pre-position the map without a visitor doing anything.

Region page
window.HandledLocator.ready().then((locator) => {
locator.searchNear({ lat: 51.5, lng: -0.12, label: "London" });
});

URL parameters and embed attributes

Links can carry the same state without any script: loc_near, loc_q, loc_tags and loc_radius on the locator page's URL. The pre-filled search guide covers them. When both are present, a method call made after load wins over the URL, because it is the later instruction.

An embed can also be locked to a subset of locations with data-tags on the script tag, and started near a place with data-near; see a page locked to some locations. A locked subset cannot be widened by setFilters or clearFilters.

TypeScript

The element type is exported from the widget package for projects that want it, and the shape is small enough to declare by hand:

Declaration
interface HandledLocatorElement extends HTMLElement {
search(text: string): Promise<void>;
searchNear(place: string | { lat: number; lng: number; label?: string }): Promise<boolean>;
setFilters(tags: string[]): Promise<string[]>;
clearFilters(): Promise<void>;
openLocation(id: string): Promise<boolean>;
closeLocation(): Promise<void>;
locate(): Promise<boolean>;
reset(): Promise<void>;
getState(): HandledLocatorState;
setLanguage(code: string | null): Promise<string | null>;
getLocations(options?: { all?: boolean }): Promise<HandledLocatorLocation[]>;
ready(): Promise<HandledLocatorElement>;
}

What the methods do not do

They do not change settings, labels or styling, which belong to the dashboard and to the MCP server, and they do not create or edit locations, which is the Location API. There is one locator per account, so there is no method for mounting a second, differently configured widget on the same page.