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.
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.
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.
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. Resolvestruewhen the locator has a point to measure from andfalsewhen 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. Resolvesfalsewhen the id is not in the loaded set. Ids come from the Location API or from thehandled:location-selectedevent.closeLocation()closes the open pane or popup.locate()asks the browser for the visitor's position and searches near it. Resolvestruewhen 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 andreadyisfalse.setLanguage(code)shows the interface in one of the account's languages. Resolves with the language that matched,frforfr-CAwhen onlyfrexists, ornullwhen none did. Passnullto 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 adistanceKmfrom 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.
{
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.
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.
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.
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:
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.