Ressourcen/

Custom CMS Bar-Chart

Schritt 1: Webflow Struktur einfügen

Schritt 2: Externe Scripte einfügen

HTML

Schritt
1
: CSS Code einfügen

CSS

 
<style>
	.na_chart_ui_control.is--active {
  	color: #ffff;
  }
  .na_chart_ui_control.is--active:hover {
  	background-color: #5762d5;
  }
</style>

Schritt
2
: Javascript Code einfügen

JavaScript


<script>
window.Webflow ||= [];
window.Webflow.push(() => {
  const container = document.querySelector('[data-chart="list"]');
  if (!container) return;

  const items = container.querySelectorAll('[data-chart="item"]');
  if (!items.length) return;

  const rawData = Array.from(items).map((item) => {
    const value = parseFloat(item.getAttribute("data-chart-value")) || 0;
    const dateStr = item.getAttribute("data-chart-date");
    const date = new Date(dateStr);
    return { date, value };
  });
  let drillLevel = 1;

  const formatLabel = (date, level) => {
    if (level === 0) return date.getFullYear().toString();
    if (level === 1)
      return date.toLocaleString("en-US", { month: "short", year: "numeric" });
    if (level === 2)
      return date.toLocaleDateString("de-DE", {
        day: "2-digit",
        month: "2-digit",
        year: "2-digit",
      });
  };

  const getKey = (date, level) => {
    if (level === 0) return `${date.getFullYear()}`;
    if (level === 1)
      return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(
        2,
        "0"
      )}`;
    if (level === 2)
      return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(
        2,
        "0"
      )}-${String(date.getDate()).padStart(2, "0")}`;
  };

  const aggregateData = (level) => {
    const grouped = {};
    rawData.forEach(({ date, value }) => {
      if (isNaN(date)) return;
      const key = getKey(date, level);
      grouped[key] = (grouped[key] || 0) + value;
    });
    return grouped;
  };

  const renderChart = (playAnimation = true) => {
    const aggregated = aggregateData(drillLevel);
    const values = Object.values(aggregated);
    const maxValue = Math.max(...values);
    if (maxValue === 0) return;

    const sortedKeys = Object.keys(aggregated).sort();

    sortedKeys.forEach((key, index) => {
      const totalValue = aggregated[key];
      const heightPercent = (totalValue / maxValue) * 100;

      const item = items[index];
      if (!item) return;

      const bar = item.querySelector('[data-chart="bar"]');
      if (bar) bar.style.height = `${heightPercent}%`;

      item.setAttribute("data-chart-date", key);
      item.setAttribute("data-chart-value", totalValue);

      const parts = key.split("-");
      let date;
      if (drillLevel === 0) date = new Date(parseInt(parts[0]), 0, 1);
      else if (drillLevel === 1)
        date = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, 1);
      else
        date = new Date(
          parseInt(parts[0]),
          parseInt(parts[1]) - 1,
          parseInt(parts[2])
        );

      const labelEl = item.querySelector('[data-chart="label"]');
      if (labelEl) labelEl.textContent = formatLabel(date, drillLevel);

      const valueEl = item.querySelector('[data-chart="value"]');
      if (valueEl) valueEl.textContent = totalValue.toLocaleString("en-US");

      item.style.display = "";

      if (playAnimation) {
        const animationName = item.getAttribute("data-chart-animation");
        if (animationName) {
          try {
            const wfIx = Webflow.require("ix3");
            wfIx.emit(animationName);
          } catch (err) {}
        }
      }
    });

    for (let i = sortedKeys.length; i < items.length; i++) {
      items[i].style.display = "none";
    }

    updateControls();
  };

  const updateControls = () => {
    const upBtn = document.querySelector('[data-chart-control="drill-up"]');
    const downBtn = document.querySelector('[data-chart-control="drill-down"]');

    if (upBtn) {
      upBtn.setAttribute("aria-label", "drill up");
      if (drillLevel > 0) upBtn.classList.add("is--active");
      else upBtn.classList.remove("is--active");
    }

    if (downBtn) {
      downBtn.setAttribute("aria-label", "drill down");
      if (drillLevel < 2) downBtn.classList.add("is--active");
      else downBtn.classList.remove("is--active");
    }
  };
  renderChart(true);

  const controls = document.querySelectorAll("[data-chart-control]");
  controls.forEach((btn) => {
    btn.addEventListener("click", () => {
      const action = btn.getAttribute("data-chart-control");
      if (action === "drill-down" && drillLevel < 2) {
        drillLevel++;
        renderChart(true);
      } else if (action === "drill-up" && drillLevel > 0) {
        drillLevel--;
        renderChart(true);
      }
    });
  });
});
</script>

Wichtige Notiz

Bei dieser Umsetzung handelt es sich um ein kleines Experiment. Daher können Fehler, Bugs und weitere Probleme auftreten, die bei der initialen Umsetzung nicht beachtet wurde.

Meine Empfehlung ist es, den Komponenten am besten als ganzes zu Übernehmen und nur kleine Anpassungen vorzunehmen.

Alternativ kannst du den Code als Übung auch gerne übernehmen und weiter anpassen, sodass eine umfassender Lösung daraus wird.

Einstellungen & Variablen

Der Code dieser Umsetzung ist so konfiguriert, dass fast alle Einstellungen über Attribute auf den jeweiligen Elementen angepasst werden können.

  • data-chart="list": Wird auf die Collection List gesetzt und definiert den gesamten Chart‑Container.
  • data-chart="item": Wird auf das Collection Item gesetzt und repräsentiert eine einzelne Säule im Chart.
  • data-chart-date="YYYY-MM-DD": Wird auf dem Item gesetzt und enthält das Datum im Format YYYY-MM-DD, das für die Aggregation genutzt wird.
  • data-chart-value="1234": Wird auf dem Item gesetzt und enthält den numerischen Wert, der die Höhe der Säule bestimmt.
  • data-chart="bar": Wird auf ein Div im Item gesetzt und zeigt die Säule, deren Höhe dynamisch berechnet wird.
  • data-chart="label": Wird auf ein Textelement gesetzt und zeigt das formatierte Datum (Jahr, Monat oder Tag) abhängig vom Drill‑Level.
  • data-chart="value": Wird auf ein Textelement gesetzt und zeigt den aggregierten Zahlenwert der Säule.
  • data-chart-animation="Name"(optional): Wird auf dem Item gesetzt und definiert den Namen einer Webflow IX3 Animation, die beim Laden und bei Drill‑Aktionen abgespielt wird.
  • data-chart-control="drill-up": Wird auf einen Button gesetzt und ermöglicht die Navigation eine Ebene höher; erhält automatisch aria-label="drill up" und die Klasse .is--active, wenn aktiv.
  • data-chart-control="drill-down": Wird auf einen Button gesetzt und ermöglicht die Navigation eine Ebene tiefer; erhält automatisch aria-label="drill down" und die Klasse .is--active, wenn aktiv.
  • .is--active: Klasse, die automatisch auf Drill‑Buttons gesetzt wird, wenn die jeweilige Aktion möglich ist.
  • .na_responsive-code: HTML‑Embed, das zusätzliche responsive Anpassungen für das Chart‑Layout enthält.

Häufige Fragen & Problem

No items found.

Mehr Tools, mehr Know-how, für dein Postfach

Erhalte Updates, Tools, Veröffentlichungen und frische Tipps jede Woche bequem per E-Mail.

Hol' dir den Webflow Boost

Trete dem Newsletter bei und erhalte wöchentlich exklusive Tipps, Tutorials & News direkt in dein Postfach.

Beitritt erfolgreich! :)
Etwas ist schief gelaufen :(