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.

Open this example on its own

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, scene or load.

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>

Read more