Popup commands and events
Click a city and its popup offers Add to shortlist - an action this page added. Choosing it raises an event carrying the feature, and the page builds a shortlist from it. The log underneath shows the events as they arrive; note the reason on each.
What to notice
- Only an id and a plain-text title cross into the map - never HTML or script.
- Nothing is sent until you subscribe, so an embed that only displays a map costs nothing extra.
- Every state event carries
reason:user,command,bookmark,tour,sceneorload.
The code
The complete page. Swap in your own publish id, and ask the map's author to add your site to its allowed origins.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Popup commands and events - Icon Map Embed API</title>
<link rel="stylesheet" href="https://www.icon-map.com/embed-examples/examples.css"> <!-- demo styling only -->
</head>
<body>
<div class="ex-main">
<div class="ex-side">
<h2>Shortlist</h2>
<ul class="ex-list" id="shortlist"><li style="cursor:default"><span>Click a city, then "Add to shortlist".</span></li></ul>
<h2>Events</h2>
<pre class="ex-log" id="log"></pre>
</div>
<div id="map"></div>
</div>
<script src="https://www.icon-map.com/js/iconmap-embed/2/iconmap-embed.umd.min.js"></script>
<script>
const el = document.getElementById("map");
const map = IconMapEmbed.embed(el, {
publishId: "pub_...",
// The map is a globe: let this page's own background show in the space around it.
background: "transparent",
chrome: { layerControl: false, bookmarksBar: false, legend: false, tourTransport: false },
visibility: { visuals: { "chart-continent": false, "slicer-continent": false, "slicer-size": false, "slicer-capital": false } },
});
map.ready.catch((error) => console.error(error.code, error.message));
// --- your own action in the feature popup -------------------------------------------
// Only an id and a plain-text title cross into the map - never HTML or script.
map.addMenuCommand({ id: "shortlist", title: "Add to shortlist", layerIds: ["cities"] });
const shortlist = new Map();
map.on("menuCommandTriggered", ({ commandId, featureKey, fields }) => {
if (commandId !== "shortlist") return;
shortlist.set(featureKey, fields);
const host = document.getElementById("shortlist");
host.textContent = "";
for (const [key, f] of shortlist) {
const row = document.createElement("li");
row.innerHTML = `<span>${f.city}</span><span>${(Number(f.population) / 1e6).toFixed(1)}M</span>`;
row.onclick = () => map.selectByKey({ layerId: "cities", keys: [key], zoomTo: true });
host.append(row);
}
// Paint the whole shortlist without disturbing what the reader selected.
map.highlight({ layerId: "cities", keys: [...shortlist.keys()] });
});
// --- every event carries a reason, so a two-way sync can ignore its own echo --------
const log = (name, text) => {
const line = document.createElement("div");
line.innerHTML = `<b>${name}</b> ${text}`;
document.getElementById("log").prepend(line);
};
map.on("viewChanged", (v) => log("viewChanged", `${v.reason} - z${v.zoom.toFixed(1)}`));
map.on("featureClick", (f) => log("featureClick", f ? `${f.layerId}: ${f.fields.city ?? f.fields.country}` : "empty map"));
map.on("selectionChanged", (s) => log("selectionChanged", `${s.reason} - ${Object.values(s.selection).flat().length} selected`));
map.on("visibilityChanged", (v) => log("visibilityChanged", v.reason));
map.on("rendered", (r) => log("rendered", `first frame in ${r.durationMs} ms`));
</script>
</body>
</html>