Filter from your page

Declared filters are the inputs the map's author chose to offer a hosting page. They are the one place a value from your page reaches the map's data, so the rule is strict: a name the author did not declare is refused before anything is applied. Press Try an undeclared filter to see it.

Open this example on its own

What to notice

  • getFilterSchema() tells you the names, types and operators - build your UI from it.
  • A list value means "any of"; numbers take { low, high }, dates { from, to } or a relative window such as { operator: "inLast", count: 30, unit: "days" }.
  • When a map is published, a filter is declared automatically for every column one of its slicers can reach.
  • This page hides every card and still lists each filter's values: getSlicerValues answers for a hidden slicer.
  • Filters narrow what the map shows; they are not a security boundary between users.

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>Filter from your page - 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">
      <div id="filters">loading...</div>
      <h2>Applied</h2>
      <pre class="ex-readout" id="applied">{}</pre>
      <div class="ex-chips" style="margin-top:10px">
        <button id="clear">Clear filters</button>
        <button id="bad">Try an undeclared filter</button>
      </div>
      <pre class="ex-readout" id="error" style="margin-top:8px;color:#ff8a8a"></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: { legend: false, layerControl: false, bookmarksBar: false, tourTransport: false },
      // Every card hidden: this page owns the filter UI. (getSlicerValues still answers for a hidden slicer.)
      visibility: { visuals: { "chart-continent": false, "slicer-continent": false, "slicer-size": false, "slicer-capital": false } },
      events: ["filtersChanged"],
    });
    map.ready.catch((error) => console.error(error.code, error.message));

    const values = {};   // filter name -> Set

    (async () => {
      // The filters the map's AUTHOR declared. A page can set these and nothing else.
      const schema = await map.getFilterSchema();
      const host = document.getElementById("filters");
      host.textContent = schema.length ? "" : "This map declares no filters.";

      // Value lists: from the schema when the author fixed them, else from the slicer on the same column.
      const slicers = await map.getSlicers();
      for (const filter of schema) {
        values[filter.name] = new Set();
        let options = filter.values;
        const slicer = slicers.find((s) => s.field === filter.column);
        if (!options && slicer) options = (await map.getSlicerValues({ slicerId: slicer.id })).values;

        host.append(Object.assign(document.createElement("h2"), { textContent: filter.label ?? slicer?.name ?? filter.name }));
        const chips = Object.assign(document.createElement("div"), { className: "ex-chips" });
        for (const option of options ?? []) {
          const chip = Object.assign(document.createElement("button"), { textContent: String(option).replace(/^\d\. /, "") });
          chip.onclick = () => {
            const set = values[filter.name];
            set.has(option) ? set.delete(option) : set.add(option);
            chip.classList.toggle("on", set.has(option));
            apply();
          };
          chips.append(chip);
        }
        host.append(chips);
      }
    })();

    // setFilters replaces every filter value at once; a list means "any of".
    function apply() {
      const filters = {};
      for (const [name, set] of Object.entries(values)) if (set.size) filters[name] = [...set];
      return map.setFilters(filters).catch(show);
    }

    document.getElementById("clear").onclick = () => {
      for (const set of Object.values(values)) set.clear();
      for (const chip of document.querySelectorAll(".ex-chips button.on")) chip.classList.remove("on");
      map.clearFilters();
    };

    // A name the author did not declare is refused before anything is applied.
    document.getElementById("bad").onclick = () => map.setFilters({ salary: [100000] }).catch(show);

    function show(error) {
      document.getElementById("error").textContent = `${error.code}\n${error.message}`;
    }
    map.on("filtersChanged", ({ filters, reason }) => {
      document.getElementById("error").textContent = "";
      document.getElementById("applied").textContent = `${JSON.stringify(filters, null, 1)}\n(${reason})`;
    });
  </script>
</body>
</html>

Read more